Implement Phase 6R-P browser webcam shell

This commit is contained in:
axiomlogicnexus 2026-05-23 01:55:29 +02:00
parent 1237daa434
commit 3c1a025e3c
10 changed files with 994 additions and 25 deletions

View file

@ -292,6 +292,49 @@ namespace HyperTwistContractLibraryInternal
return Profile;
}
FString NormalizeBrowserFaceHint(const FString& FaceId)
{
FString Normalized = FaceId;
Normalized = Normalized.TrimStartAndEnd();
Normalized.ToUpperInline();
return Normalized;
}
FHyperTwistVisionBrowserShellProfile MakeDefaultClassicCubeBrowserShellProfile(const FString& ProfileId)
{
FHyperTwistVisionBrowserShellProfile Profile;
Profile.BrowserShellProfileId = ProfileId;
Profile.TransportKind = TEXT("websocket");
Profile.TransportEndpoint = TEXT("ws://localhost:8090/");
Profile.CaptureIntervalMs = 100;
Profile.bSupportsManualCubeEdit = true;
Profile.bSupportsPlaybackStage = false;
Profile.OrderedStageIds = {
TEXT("home-page"),
TEXT("video-stream"),
TEXT("cube-edit")
};
auto AddAction = [&Profile](
const TCHAR* ActionId,
const TCHAR* InputBinding,
const TCHAR* SurfaceId
)
{
FHyperTwistVisionShellActionBinding ActionBinding;
ActionBinding.ActionId = ActionId;
ActionBinding.InputBinding = InputBinding;
ActionBinding.SurfaceId = SurfaceId;
Profile.ActionBindings.Add(ActionBinding);
};
AddAction(TEXT("start-browser-capture"), TEXT("button:start-capture"), TEXT("home-page"));
AddAction(TEXT("finish-browser-capture"), TEXT("button:finish-capture"), TEXT("video-stream"));
AddAction(TEXT("apply-manual-corrections"), TEXT("button:apply-corrections"), TEXT("cube-edit"));
AddAction(TEXT("restart-browser-session"), TEXT("button:restart-session"), TEXT("cube-edit"));
return Profile;
}
void ResolveMockClassicFaceSwatch(
const FString& FaceId,
FString& OutColorId,
@ -499,6 +542,64 @@ namespace HyperTwistContractLibraryInternal
return ShellState;
}
FHyperTwistVisionBrowserShellState MakeClassicCubeBrowserShellState(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
const FHyperTwistVisionFaceObservation& ObservedFace
)
{
FHyperTwistVisionBrowserShellState BrowserShellState;
const FHyperTwistVisionBrowserShellProfile BrowserShellProfile =
SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid()
? SessionConfig.BrowserShellProfileDefinition
: MakeDefaultClassicCubeBrowserShellProfile(
!SessionConfig.BrowserShellProfileId.IsEmpty()
? SessionConfig.BrowserShellProfileId
: TEXT("classic-cube-browser-shell-v1")
);
BrowserShellState.BrowserShellProfileId = BrowserShellProfile.BrowserShellProfileId;
BrowserShellState.TransportKind = BrowserShellProfile.TransportKind;
BrowserShellState.TransportEndpoint = BrowserShellProfile.TransportEndpoint;
BrowserShellState.CaptureIntervalMs = BrowserShellProfile.CaptureIntervalMs;
BrowserShellState.CubeDimension = 3;
BrowserShellState.ExpectedFaceCount = 6;
BrowserShellState.CommittedFaceCount = ReconstructionSession.IsStructurallyValid()
? FMath::Max(0, ReconstructionSession.TotalCommittedFaceCount)
: 0;
BrowserShellState.TargetFaceHint = !ObservedFace.FaceId.IsEmpty()
? NormalizeBrowserFaceHint(ObservedFace.FaceId)
: TEXT("F");
BrowserShellState.bTransportRouteReady = true;
BrowserShellState.bPlaybackDeferred = !BrowserShellProfile.bSupportsPlaybackStage;
BrowserShellState.bSnapshotReady = ObservedFace.IsStructurallyValid() && ObservedFace.bCommitReady;
if (ReconstructionSession.bHasCompleteClassicCubeNet)
{
BrowserShellState.ActiveStageId = TEXT("cube-edit");
BrowserShellState.bVideoStreamVisible = false;
BrowserShellState.bManualCubeEditVisible = true;
BrowserShellState.bManualCubeEditReady = true;
}
else
{
BrowserShellState.ActiveStageId = TEXT("video-stream");
BrowserShellState.bVideoStreamVisible = true;
BrowserShellState.bManualCubeEditVisible = false;
BrowserShellState.bManualCubeEditReady = false;
}
if (BrowserShellState.TargetFaceHint.IsEmpty()
&& ReconstructionSession.IsStructurallyValid()
&& ReconstructionSession.MissingFaces.Num() > 0)
{
BrowserShellState.TargetFaceHint =
NormalizeBrowserFaceHint(ReconstructionSession.MissingFaces[0]);
}
return BrowserShellState;
}
}
FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition()
@ -733,6 +834,11 @@ FHyperTwistVisionSessionConfig UHyperTwistContractLibrary::MakeSampleVisionSessi
HyperTwistContractLibraryInternal::MakeDefaultClassicCubeWebcamShellProfile(
Config.ShellProfileId
);
Config.BrowserShellProfileId = TEXT("classic-cube-browser-shell-v1");
Config.BrowserShellProfileDefinition =
HyperTwistContractLibraryInternal::MakeDefaultClassicCubeBrowserShellProfile(
Config.BrowserShellProfileId
);
Config.FrameSize = FIntPoint(1280, 720);
Config.MaxFrameRate = 30;
return Config;
@ -759,6 +865,11 @@ FHyperTwistVisionPreviewResult UHyperTwistContractLibrary::MakeMockVisionPreview
Result.ObservedFace.FaceId,
FString()
);
Result.BrowserShellState = HyperTwistContractLibraryInternal::MakeClassicCubeBrowserShellState(
UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(),
FHyperTwistVisionReconstructionSession(),
Result.ObservedFace
);
Result.Guidance = TEXT("capture-ready");
return Result;
}
@ -793,6 +904,11 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe
Result.ObservedFace.FaceId,
Result.CommittedUnit
);
Result.BrowserShellState = HyperTwistContractLibraryInternal::MakeClassicCubeBrowserShellState(
UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(),
Result.ReconstructionSession,
Result.ObservedFace
);
return Result;
}

View file

@ -2436,6 +2436,41 @@ namespace HyperTwistTrainingSubsystemInternal
return Profile;
}
FHyperTwistVisionBrowserShellProfile BuildDefaultClassicCubeBrowserShellProfile(const FString& ProfileId)
{
FHyperTwistVisionBrowserShellProfile Profile;
Profile.BrowserShellProfileId = ProfileId;
Profile.TransportKind = TEXT("websocket");
Profile.TransportEndpoint = TEXT("ws://localhost:8090/");
Profile.CaptureIntervalMs = 100;
Profile.bSupportsManualCubeEdit = true;
Profile.bSupportsPlaybackStage = false;
Profile.OrderedStageIds = {
TEXT("home-page"),
TEXT("video-stream"),
TEXT("cube-edit")
};
auto AddAction = [&Profile](
const TCHAR* ActionId,
const TCHAR* InputBinding,
const TCHAR* SurfaceId
)
{
FHyperTwistVisionShellActionBinding ActionBinding;
ActionBinding.ActionId = ActionId;
ActionBinding.InputBinding = InputBinding;
ActionBinding.SurfaceId = SurfaceId;
Profile.ActionBindings.Add(ActionBinding);
};
AddAction(TEXT("start-browser-capture"), TEXT("button:start-capture"), TEXT("home-page"));
AddAction(TEXT("finish-browser-capture"), TEXT("button:finish-capture"), TEXT("video-stream"));
AddAction(TEXT("apply-manual-corrections"), TEXT("button:apply-corrections"), TEXT("cube-edit"));
AddAction(TEXT("restart-browser-session"), TEXT("button:restart-session"), TEXT("cube-edit"));
return Profile;
}
FHyperTwistVisionShellState BuildClassicCubeWebcamShellState(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
@ -2487,6 +2522,62 @@ namespace HyperTwistTrainingSubsystemInternal
return ShellState;
}
FHyperTwistVisionBrowserShellState BuildClassicCubeBrowserShellState(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
const FHyperTwistVisionFaceObservation& ObservedFace
)
{
FHyperTwistVisionBrowserShellState BrowserShellState;
const FHyperTwistVisionBrowserShellProfile BrowserShellProfile =
SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid()
? SessionConfig.BrowserShellProfileDefinition
: BuildDefaultClassicCubeBrowserShellProfile(
!SessionConfig.BrowserShellProfileId.IsEmpty()
? SessionConfig.BrowserShellProfileId
: TEXT("classic-cube-browser-shell-v1")
);
BrowserShellState.BrowserShellProfileId = BrowserShellProfile.BrowserShellProfileId;
BrowserShellState.TransportKind = BrowserShellProfile.TransportKind;
BrowserShellState.TransportEndpoint = BrowserShellProfile.TransportEndpoint;
BrowserShellState.CaptureIntervalMs = BrowserShellProfile.CaptureIntervalMs;
BrowserShellState.CubeDimension = 3;
BrowserShellState.CommittedFaceCount = ReconstructionSession.IsStructurallyValid()
? FMath::Max(0, ReconstructionSession.TotalCommittedFaceCount)
: 0;
BrowserShellState.ExpectedFaceCount = 6;
BrowserShellState.TargetFaceHint = !ObservedFace.FaceId.IsEmpty()
? NormalizeRecognitionFaceId(ObservedFace.FaceId)
: TEXT("F");
BrowserShellState.bTransportRouteReady = true;
BrowserShellState.bPlaybackDeferred = !BrowserShellProfile.bSupportsPlaybackStage;
BrowserShellState.bSnapshotReady = ObservedFace.IsStructurallyValid() && ObservedFace.bCommitReady;
if (ReconstructionSession.bHasCompleteClassicCubeNet)
{
BrowserShellState.ActiveStageId = TEXT("cube-edit");
BrowserShellState.bVideoStreamVisible = false;
BrowserShellState.bManualCubeEditVisible = true;
BrowserShellState.bManualCubeEditReady = true;
}
else
{
BrowserShellState.ActiveStageId = TEXT("video-stream");
BrowserShellState.bVideoStreamVisible = true;
BrowserShellState.bManualCubeEditVisible = false;
BrowserShellState.bManualCubeEditReady = false;
}
if (BrowserShellState.TargetFaceHint.IsEmpty() && ReconstructionSession.MissingFaces.Num() > 0)
{
BrowserShellState.TargetFaceHint =
NormalizeRecognitionFaceId(ReconstructionSession.MissingFaces[0]);
}
return BrowserShellState;
}
EHyperTwistPuzzleFamily ResolvePuzzleFamilyFromPuzzleId(const FString& PuzzleId)
{
FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition;
@ -6771,6 +6862,11 @@ FHyperTwistVisionPreviewResult UHyperTwistTrainingSubsystem::SubmitActiveRecogni
!Result.ObservedFace.FaceId.IsEmpty() ? Result.ObservedFace.FaceId : NormalizedFrame.TargetFaceHint,
FString()
);
Result.BrowserShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeBrowserShellState(
ActiveRecognitionSessionState.SessionConfig,
ActiveRecognitionSessionState.ActiveReconstructionSession,
Result.ObservedFace
);
}
ActiveRecognitionSessionState.bHasPreviewResult = true;
@ -6911,6 +7007,11 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit
Result.ObservedFace.FaceId,
Result.CommittedUnit
);
Result.BrowserShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeBrowserShellState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace
);
}
const FString ResolvedStage = !Result.CommittedUnit.IsEmpty()
@ -7861,6 +7962,28 @@ FHyperTwistVisionSessionConfig UHyperTwistTrainingSubsystem::BuildActiveRecognit
{
SessionConfig.ShellProfileDefinition.ShellProfileId = SessionConfig.ShellProfileId;
}
if (SessionConfig.BrowserShellProfileId.IsEmpty()
&& SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid())
{
SessionConfig.BrowserShellProfileId =
SessionConfig.BrowserShellProfileDefinition.BrowserShellProfileId;
}
if (SessionConfig.BrowserShellProfileId.IsEmpty())
{
SessionConfig.BrowserShellProfileId = TEXT("classic-cube-browser-shell-v1");
}
if (!SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid())
{
SessionConfig.BrowserShellProfileDefinition =
HyperTwistTrainingSubsystemInternal::BuildDefaultClassicCubeBrowserShellProfile(
SessionConfig.BrowserShellProfileId
);
}
if (SessionConfig.BrowserShellProfileDefinition.BrowserShellProfileId.IsEmpty())
{
SessionConfig.BrowserShellProfileDefinition.BrowserShellProfileId =
SessionConfig.BrowserShellProfileId;
}
}
return SessionConfig;

View file

@ -531,6 +531,133 @@ struct FHyperTwistVisionShellState
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionBrowserShellProfile
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString BrowserShellProfileId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TransportKind = TEXT("websocket");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TransportEndpoint = TEXT("ws://localhost:8090/");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CaptureIntervalMs = 100;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSupportsManualCubeEdit = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSupportsPlaybackStage = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> OrderedStageIds;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionShellActionBinding> ActionBindings;
bool IsStructurallyValid() const
{
if (BrowserShellProfileId.IsEmpty()
|| TransportKind.IsEmpty()
|| TransportEndpoint.IsEmpty()
|| CaptureIntervalMs <= 0
|| OrderedStageIds.Num() <= 0
|| ActionBindings.Num() <= 0)
{
return false;
}
for (const FString& StageId : OrderedStageIds)
{
if (StageId.IsEmpty())
{
return false;
}
}
for (const FHyperTwistVisionShellActionBinding& ActionBinding : ActionBindings)
{
if (!ActionBinding.IsStructurallyValid())
{
return false;
}
}
return true;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionBrowserShellState
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString BrowserShellProfileId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActiveStageId = TEXT("home-page");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TransportKind = TEXT("websocket");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TransportEndpoint = TEXT("ws://localhost:8090/");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CaptureIntervalMs = 100;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CubeDimension = 3;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CommittedFaceCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ExpectedFaceCount = 6;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TargetFaceHint;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bTransportRouteReady = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bVideoStreamVisible = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bManualCubeEditVisible = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bManualCubeEditReady = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bPlaybackDeferred = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSnapshotReady = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSessionRestartVisible = true;
bool IsStructurallyValid() const
{
return !BrowserShellProfileId.IsEmpty()
&& !ActiveStageId.IsEmpty()
&& !TransportKind.IsEmpty()
&& !TransportEndpoint.IsEmpty()
&& CaptureIntervalMs > 0
&& CubeDimension > 0
&& CommittedFaceCount >= 0
&& ExpectedFaceCount > 0;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionSessionConfig
{
@ -566,6 +693,12 @@ struct FHyperTwistVisionSessionConfig
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionShellProfile ShellProfileDefinition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString BrowserShellProfileId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionBrowserShellProfile BrowserShellProfileDefinition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FIntPoint FrameSize = FIntPoint(1280, 720);
@ -632,6 +765,9 @@ struct FHyperTwistVisionPreviewResult
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionShellState ShellState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionBrowserShellState BrowserShellState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Guidance;
};
@ -683,6 +819,9 @@ struct FHyperTwistVisionCommitResult
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionShellState ShellState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionBrowserShellState BrowserShellState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> Warnings;

View file

@ -0,0 +1,250 @@
// Copyright HyperTwist, Inc. All Rights Reserved.
#include "Misc/AutomationTest.h"
#include "Engine/GameInstance.h"
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
#include "UObject/UnrealType.h"
#if WITH_AUTOMATION_TESTS
namespace HyperTwistRubixCubeSolverPhase6RPTestInternal
{
FHyperTwistTrainingDeck MakeRecognitionDeck()
{
FHyperTwistTrainingDeck Deck;
Deck.DeckId = TEXT("phase6r-p/rubix-cube-solver-browser-shell");
Deck.Title = TEXT("Phase 6R-P Rubix Cube Solver Browser Shell");
Deck.DeliveryModes = {
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
EHyperTwistTrainingDeliveryMode::CoachReviewed
};
FHyperTwistTrainingCase TrainingCase;
TrainingCase.CaseId = TEXT("phase6r-p-case");
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition;
TrainingCase.PromptLabel = TEXT("Phase 6R-P Recognition");
TrainingCase.AllowedDeliveryModes = {
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
EHyperTwistTrainingDeliveryMode::CoachReviewed
};
Deck.Cases = {TrainingCase};
return Deck;
}
void ForceMockRecognitionClient(UHyperTwistTrainingSubsystem* TrainingSubsystem)
{
if (TrainingSubsystem == nullptr)
{
return;
}
if (FStrProperty* RecognitionClientKindProperty = FindFProperty<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-p-user"),
SessionId,
EHyperTwistTrainingDeliveryMode::RecognitionAssisted
);
return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr;
}
void SubmitPreviewFrame(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& FaceId, const int32 FrameOrdinal)
{
FHyperTwistVisionFrameEnvelope Frame;
Frame.FrameOrdinal = FrameOrdinal;
Frame.TargetFaceHint = FaceId;
TrainingSubsystem->SubmitActiveRecognitionFrame(Frame);
}
FHyperTwistVisionCommitResult CommitFace(
UHyperTwistTrainingSubsystem* TrainingSubsystem,
const FString& FaceId,
const int32 FrameOrdinal
)
{
SubmitPreviewFrame(TrainingSubsystem, FaceId, FrameOrdinal);
FHyperTwistVisionCommitRequest Request;
Request.TargetFace = FaceId;
return TrainingSubsystem->CommitActiveRecognitionObservation(Request);
}
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RPBrowserShellSessionConfigTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.P.BrowserShellSessionConfig",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RPBrowserShellSessionConfigTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RPTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-p-config-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-P."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
FString OpenError;
TestTrue(TEXT("The recognition session must open for the browser-shell route."), TrainingSubsystem->OpenActiveRecognitionSession(OpenError));
TestTrue(TEXT("Opening the recognition session must not report an error."), OpenError.IsEmpty());
const FHyperTwistTrainingRecognitionSessionState SessionState =
TrainingSubsystem->GetActiveRecognitionSessionState();
TestEqual(
TEXT("The browser shell route must normalize the first-party browser profile id."),
SessionState.SessionConfig.BrowserShellProfileId,
TEXT("classic-cube-browser-shell-v1")
);
TestTrue(
TEXT("The browser shell route must provide a structurally valid browser shell profile definition."),
SessionState.SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid()
);
TestEqual(
TEXT("The browser shell must retain three ordered browser stages."),
SessionState.SessionConfig.BrowserShellProfileDefinition.OrderedStageIds.Num(),
3
);
TestEqual(
TEXT("The browser shell must retain four bounded browser actions."),
SessionState.SessionConfig.BrowserShellProfileDefinition.ActionBindings.Num(),
4
);
TestEqual(
TEXT("The browser shell must keep the websocket transport kind."),
SessionState.SessionConfig.BrowserShellProfileDefinition.TransportKind,
TEXT("websocket")
);
TestEqual(
TEXT("The browser shell must keep the local websocket endpoint."),
SessionState.SessionConfig.BrowserShellProfileDefinition.TransportEndpoint,
TEXT("ws://localhost:8090/")
);
TestEqual(
TEXT("The browser shell must preserve the donor-like capture interval."),
SessionState.SessionConfig.BrowserShellProfileDefinition.CaptureIntervalMs,
100
);
TestTrue(
TEXT("The browser shell must allow manual cube edit in the bounded route."),
SessionState.SessionConfig.BrowserShellProfileDefinition.bSupportsManualCubeEdit
);
TestFalse(
TEXT("The browser shell must still defer playback-stage ownership in this packet."),
SessionState.SessionConfig.BrowserShellProfileDefinition.bSupportsPlaybackStage
);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RPBrowserShellPreviewStateTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.P.BrowserShellPreviewState",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RPBrowserShellPreviewStateTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RPTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-p-preview-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-P."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
FHyperTwistVisionFrameEnvelope Frame;
Frame.TargetFaceHint = TEXT("F");
const FHyperTwistVisionPreviewResult Result = TrainingSubsystem->SubmitActiveRecognitionFrame(Frame);
TestTrue(TEXT("The preview result must carry a structurally valid browser shell state."), Result.BrowserShellState.IsStructurallyValid());
TestEqual(TEXT("The preview browser shell must preserve the first-party profile id."), Result.BrowserShellState.BrowserShellProfileId, TEXT("classic-cube-browser-shell-v1"));
TestEqual(TEXT("The preview browser shell must stay in the video-stream stage."), Result.BrowserShellState.ActiveStageId, TEXT("video-stream"));
TestEqual(TEXT("The preview browser shell must keep the websocket transport kind."), Result.BrowserShellState.TransportKind, TEXT("websocket"));
TestEqual(TEXT("The preview browser shell must keep the local websocket endpoint."), Result.BrowserShellState.TransportEndpoint, TEXT("ws://localhost:8090/"));
TestEqual(TEXT("The preview browser shell must preserve the 100ms capture interval."), Result.BrowserShellState.CaptureIntervalMs, 100);
TestEqual(TEXT("The preview browser shell must preserve the classic cube dimension."), Result.BrowserShellState.CubeDimension, 3);
TestEqual(TEXT("The preview browser shell must keep the six-face expectation."), Result.BrowserShellState.ExpectedFaceCount, 6);
TestEqual(TEXT("The preview browser shell must report no committed faces yet."), Result.BrowserShellState.CommittedFaceCount, 0);
TestEqual(TEXT("The preview browser shell must preserve the current face hint."), Result.BrowserShellState.TargetFaceHint, TEXT("F"));
TestTrue(TEXT("The preview browser shell must keep the transport route ready."), Result.BrowserShellState.bTransportRouteReady);
TestTrue(TEXT("The preview browser shell must keep the video-stream surface visible."), Result.BrowserShellState.bVideoStreamVisible);
TestFalse(TEXT("The preview browser shell must keep manual edit hidden."), Result.BrowserShellState.bManualCubeEditVisible);
TestFalse(TEXT("The preview browser shell must keep manual edit unready."), Result.BrowserShellState.bManualCubeEditReady);
TestTrue(TEXT("The preview browser shell must keep playback deferred."), Result.BrowserShellState.bPlaybackDeferred);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RPBrowserShellCompleteSessionTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.P.BrowserShellCompleteSession",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RPBrowserShellCompleteSessionTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RPTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-p-complete-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-P."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
const TArray<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 = HyperTwistRubixCubeSolverPhase6RPTestInternal::CommitFace(
TrainingSubsystem,
FaceOrder[FaceOrdinal],
FaceOrdinal + 1
);
}
TestTrue(TEXT("The completed browser-shell route must carry a structurally valid reconstruction session."), Result.ReconstructionSession.IsStructurallyValid());
TestTrue(TEXT("The completed browser-shell route must carry a structurally valid browser shell state."), Result.BrowserShellState.IsStructurallyValid());
TestTrue(TEXT("The completed reconstruction must expose a full classic cube net."), Result.ReconstructionSession.bHasCompleteClassicCubeNet);
TestEqual(TEXT("The browser shell must transition into cube-edit after a complete reconstruction."), Result.BrowserShellState.ActiveStageId, TEXT("cube-edit"));
TestEqual(TEXT("The browser shell must report six committed faces after a complete reconstruction."), Result.BrowserShellState.CommittedFaceCount, 6);
TestFalse(TEXT("The browser shell must hide the live video stream once manual correction is ready."), Result.BrowserShellState.bVideoStreamVisible);
TestTrue(TEXT("The browser shell must expose manual cube edit once the reconstruction is complete."), Result.BrowserShellState.bManualCubeEditVisible);
TestTrue(TEXT("The browser shell must mark manual cube edit ready once the reconstruction is complete."), Result.BrowserShellState.bManualCubeEditReady);
TestTrue(TEXT("The browser shell must keep playback deferred even after the reconstruction completes."), Result.BrowserShellState.bPlaybackDeferred);
return true;
}
#endif

View file

@ -151,9 +151,11 @@ Status update on `2026-05-21`:
- the bounded permissive `Phase 6R-N` `HactarCE/Hyperspeedcube` puzzle-definition DSL packet is
now landed in current code
- `HactarCE/Hyperspeedcube` is now closed for the currently justified retained families
- the current next bounded move is a source-backed `Phase 6R-P`
`vivaansinghvi07/rubix-cube-solver` browser/webcam shell preparation/control pass, not a new
restrictive packet by default
- the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
packet is now landed in current code
- the current next bounded move is a source-backed `Phase 6R-Q`
`vivaansinghvi07/rubix-cube-solver` solve explanation/recommendation preparation/control pass,
not a new restrictive packet by default
- use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A`
ownership contract for the current queue after that correction
@ -257,9 +259,9 @@ Current practical interpretation:
- the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md`
- the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md`
- the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md`
- the next bounded move is a source-backed `Phase 6R-P`
`vivaansinghvi07/rubix-cube-solver` preparation/control pass for a bounded browser/webcam shell
seam
- the next bounded move is a source-backed `Phase 6R-Q`
`vivaansinghvi07/rubix-cube-solver` preparation/control pass for a bounded solve
explanation/recommendation seam
Companion docs:
@ -796,12 +798,12 @@ Approved working posture:
- preserve the row as the bounded recognition companion lane after `kkoomen/qbr`
- if later implementation ships or copies bundled third-party frontend assets, capture and preserve
those assets' own upstream license/provenance rather than flattening them into the repo-level `MIT`
- the first bounded retained slice is now landed in current code:
- the first two bounded retained slices are now landed in current code:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- browser/webcam shell profile and browser-shell session-state composition
- keep the row only partially incorporated by default:
- browser/webcam shell remains deferred
- broad solve explanation or recommendation shell remains deferred
- any redistribution of `frontend/lib/twistysim.min.js` still requires preserved or replaced
upstream provenance/license

View file

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

View file

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

View file

@ -96,11 +96,13 @@ The next bounded move is now:
implementation packet is now landed in current code
5. the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in
current code
6. source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
preparation/control pass is now the next bounded move
7. keep `Phase 6R-P` narrower than bundled `twistysim.min.js` redistribution and broader solve
explanation/recommendation ownership in the same packet
8. keep the `qbr` guard visible:
6. the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
packet is now landed in current code
7. source-backed `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` solve
explanation/recommendation preparation/control pass is now the next bounded move
8. keep `Phase 6R-Q` narrower than bundled `twistysim.min.js` redistribution and generalized
solver backend ownership in the same packet
9. keep the `qbr` guard visible:
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation
or replacement in any future widening that would ship that asset

View file

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

View file

@ -70,8 +70,12 @@ Canonical discovery surfaces for roadmap interpretation:
consumed
- the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face
reconstruction and face-vote replacement packet is now landed in current code
- `vivaansinghvi07/rubix-cube-solver` remains partially incorporated; browser/webcam shell, broad
solve explanation, and bundled `twistysim.min.js` redistribution stay deferred
- the generic source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` control pass is now
consumed
- the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
packet is now landed in current code
- `vivaansinghvi07/rubix-cube-solver` remains partially incorporated; broad solve explanation and
bundled `twistysim.min.js` redistribution stay deferred
- the generic source-backed `Phase 6R-D` `roice3/MagicTile` control pass is now consumed
- the bounded permissive `Phase 6R-D` `roice3/MagicTile` tiling topology and geometry-family
packet is now landed in current code
@ -118,8 +122,11 @@ Canonical discovery surfaces for roadmap interpretation:
- `HactarCE/Hyperspeedcube` is now closed for the currently justified retained families
- the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in
current code
- the current next bounded move is a source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver`
browser/webcam shell preparation/control pass for one bounded recognition-shell widening slice
- the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
packet is now landed in current code
- the current next bounded move is a source-backed `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver`
solve explanation/recommendation preparation/control pass for one bounded correction-shell
widening slice
- the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
waiting on another first-party packet
@ -181,8 +188,8 @@ Current routing truth:
- active non-live implementation-board rows: `34`
- retained benchmark, oracle, or clean-room-later rows outside the active implementation board: `9`
The next bounded move is a source-backed `Phase 6R-P`
`vivaansinghvi07/rubix-cube-solver` browser/webcam shell preparation/control pass.
The next bounded move is a source-backed `Phase 6R-Q`
`vivaansinghvi07/rubix-cube-solver` solve explanation/recommendation preparation/control pass.
Queue interpretation after that packet:
@ -234,11 +241,11 @@ Queue interpretation after that packet:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- browser/webcam shell profile and browser-shell session-state composition
- still deferred:
- browser/webcam shell
- broad solve explanation or recommendation shell
- bundled `twistysim.min.js` redistribution
- after the `Phase 6R-C` implementation packet, keep the future legal sequencing guard visible:
- after the `Phase 6R-P` implementation packet, keep the future legal sequencing guard visible:
- `vivaansinghvi07/rubix-cube-solver`
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing
its upstream provenance/license in any future widening that would ship that asset
@ -306,10 +313,10 @@ Queue interpretation after that packet:
- viseme / gesture runtime integration
- broad assistant-platform scope
- the next queue shape is now:
- `vivaansinghvi07/rubix-cube-solver` deferred browser/webcam shell remainder
- `vivaansinghvi07/rubix-cube-solver` deferred solve explanation / recommendation remainder
- the next bounded packet should stay narrow:
- browser/webcam shell preparation/control before widening into bundled `twistysim.min.js`
redistribution or broad solve explanation/recommendation ownership
- solve explanation/recommendation preparation/control before widening into bundled
`twistysim.min.js` redistribution or generalized solver backend ownership
- keep the speech-input / voice sidecar legal sequencing guard visible:
- keep code-license judgments separate from model, voice, and payload-license review
- keep the provider-neutral speech-lane guard visible: