Implement Phase 6R-C reconstruction session contract

This commit is contained in:
axiomlogicnexus 2026-05-21 01:35:34 +02:00
parent 47670a764a
commit 175fdce3fb
10 changed files with 1616 additions and 37 deletions

View file

@ -131,6 +131,55 @@ namespace HyperTwistContractLibraryInternal
return Profile;
}
void ResolveMockClassicFaceSwatch(
const FString& FaceId,
FString& OutColorId,
FString& OutNotation,
FIntVector& OutSampleBgr
)
{
const FString NormalizedFaceId = FaceId.ToUpper();
if (NormalizedFaceId == TEXT("U"))
{
OutColorId = TEXT("white");
OutNotation = TEXT("U");
OutSampleBgr = FIntVector(255, 255, 255);
return;
}
if (NormalizedFaceId == TEXT("R"))
{
OutColorId = TEXT("red");
OutNotation = TEXT("R");
OutSampleBgr = FIntVector(0, 0, 255);
return;
}
if (NormalizedFaceId == TEXT("D"))
{
OutColorId = TEXT("yellow");
OutNotation = TEXT("D");
OutSampleBgr = FIntVector(0, 255, 255);
return;
}
if (NormalizedFaceId == TEXT("L"))
{
OutColorId = TEXT("orange");
OutNotation = TEXT("L");
OutSampleBgr = FIntVector(0, 165, 255);
return;
}
if (NormalizedFaceId == TEXT("B"))
{
OutColorId = TEXT("blue");
OutNotation = TEXT("B");
OutSampleBgr = FIntVector(255, 0, 0);
return;
}
OutColorId = TEXT("green");
OutNotation = TEXT("F");
OutSampleBgr = FIntVector(0, 255, 0);
}
FHyperTwistVisionFaceObservation MakeMockClassicFaceObservation(
const FString& ObservationId,
const FString& FaceId,
@ -148,21 +197,103 @@ namespace HyperTwistContractLibraryInternal
Observation.bOrderingStable = true;
Observation.bCommitReady = bCommitReady;
FString ColorId;
FString Notation;
FIntVector SampleBgr = FIntVector::ZeroValue;
ResolveMockClassicFaceSwatch(FaceId, ColorId, Notation, SampleBgr);
for (int32 GridIndex = 0; GridIndex < 9; ++GridIndex)
{
FHyperTwistVisionObservedSticker Sticker;
Sticker.GridIndex = GridIndex;
Sticker.GridRow = GridIndex / 3;
Sticker.GridColumn = GridIndex % 3;
Sticker.ColorId = TEXT("green");
Sticker.Notation = TEXT("F");
Sticker.SampleBgr = FIntVector(0, 255, 0);
Sticker.ColorId = ColorId;
Sticker.Notation = Notation;
Sticker.SampleBgr = SampleBgr;
Sticker.Confidence = StickerConfidence;
Observation.OrderedStickers.Add(Sticker);
}
return Observation;
}
FHyperTwistVisionReconstructionSession MakeMockClassicReconstructionSession(
const FString& SessionId,
const TArray<FString>& FaceIds,
const float StickerConfidence
)
{
FHyperTwistVisionReconstructionSession Session;
Session.SessionId = SessionId;
Session.PuzzleId = TEXT("cube/3x3x3");
Session.ReconstructionProfile = TEXT("classic-face-vote-v1");
Session.OrientationProfile = TEXT("classic-target-face-net-v1");
Session.bUsesObservationVoting = true;
Session.bHasStableOrientation = FaceIds.Num() > 0;
const TArray<FString> ExpectedFaces = {
TEXT("U"),
TEXT("R"),
TEXT("F"),
TEXT("D"),
TEXT("L"),
TEXT("B")
};
for (const FString& FaceId : FaceIds)
{
FHyperTwistVisionFaceObservation Observation = MakeMockClassicFaceObservation(
FString::Printf(TEXT("%s/%s"), *SessionId, *FaceId),
FaceId,
StickerConfidence,
true
);
FHyperTwistVisionCommittedFaceState FaceState;
FaceState.FaceId = FaceId;
FaceState.LatestObservationId = Observation.ObservationId;
FaceState.OrderingProfile = Observation.OrderingProfile;
FaceState.ObservationCount = 1;
FaceState.RevisionCount = 0;
FaceState.LatestOrderedStickers = Observation.OrderedStickers;
for (const FHyperTwistVisionObservedSticker& Sticker : Observation.OrderedStickers)
{
FHyperTwistVisionStickerVoteTally Tally;
Tally.Notation = Sticker.Notation;
Tally.ColorId = Sticker.ColorId;
Tally.VoteCount = 1;
FHyperTwistVisionReconstructionStickerVote Vote;
Vote.GridIndex = Sticker.GridIndex;
Vote.GridRow = Sticker.GridRow;
Vote.GridColumn = Sticker.GridColumn;
Vote.WinningNotation = Sticker.Notation;
Vote.WinningColorId = Sticker.ColorId;
Vote.WinningVoteCount = 1;
Vote.TotalVoteCount = 1;
Vote.Tallies.Add(Tally);
FaceState.StickerVotes.Add(Vote);
}
Session.CommittedFaces.Add(FaceState);
}
Session.TotalCommittedFaceCount = Session.CommittedFaces.Num();
Session.TotalRevisionCount = 0;
Session.ConfidenceRollup = StickerConfidence;
for (const FString& ExpectedFace : ExpectedFaces)
{
if (!FaceIds.Contains(ExpectedFace))
{
Session.MissingFaces.Add(ExpectedFace);
}
}
Session.bHasCompleteClassicCubeNet = Session.MissingFaces.Num() == 0;
return Session;
}
}
FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition()
@ -435,6 +566,11 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe
0.96f,
true
);
Result.ReconstructionSession = HyperTwistContractLibraryInternal::MakeMockClassicReconstructionSession(
Result.SessionId,
{TEXT("F")},
Result.Confidence
);
return Result;
}
@ -449,8 +585,13 @@ FHyperTwistVisionFinalizeResult UHyperTwistContractLibrary::MakeMockVisionFinali
Result.FinalSnapshot.SnapshotId = TEXT("snap_final");
Result.FinalSnapshot.State = Result.FinalState;
Result.FinalSnapshot.DerivedHash = TEXT("final_hash");
Result.ReplaySeedEventsJson.Add(SerializeReplayPacketToJson(MakeSampleRecognitionReplayPacket()));
Result.ConfidenceRollup = 0.97f;
Result.ReconstructionSession = HyperTwistContractLibraryInternal::MakeMockClassicReconstructionSession(
Result.SessionId,
{TEXT("U"), TEXT("R"), TEXT("F"), TEXT("D"), TEXT("L"), TEXT("B")},
Result.ConfidenceRollup
);
Result.ReplaySeedEventsJson.Add(SerializeReplayPacketToJson(MakeSampleRecognitionReplayPacket()));
return Result;
}
@ -464,7 +605,8 @@ FHyperTwistVisionServiceHealth UHyperTwistContractLibrary::MakeMockVisionService
TEXT("liveCamera3x3"),
TEXT("faceCommitFlow"),
TEXT("calibrationProfiles"),
TEXT("orderedFaceObservation")
TEXT("orderedFaceObservation"),
TEXT("reconstructionSession")
};
Health.bReady = true;
return Health;

View file

@ -2,6 +2,78 @@
#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"
namespace HyperTwistMockVisionClientInternal
{
void ResolveMockClassicFaceSwatch(
const FString& FaceId,
FString& OutColorId,
FString& OutNotation,
FIntVector& OutSampleBgr
)
{
const FString NormalizedFaceId = FaceId.ToUpper();
if (NormalizedFaceId == TEXT("U"))
{
OutColorId = TEXT("white");
OutNotation = TEXT("U");
OutSampleBgr = FIntVector(255, 255, 255);
return;
}
if (NormalizedFaceId == TEXT("R"))
{
OutColorId = TEXT("red");
OutNotation = TEXT("R");
OutSampleBgr = FIntVector(0, 0, 255);
return;
}
if (NormalizedFaceId == TEXT("D"))
{
OutColorId = TEXT("yellow");
OutNotation = TEXT("D");
OutSampleBgr = FIntVector(0, 255, 255);
return;
}
if (NormalizedFaceId == TEXT("L"))
{
OutColorId = TEXT("orange");
OutNotation = TEXT("L");
OutSampleBgr = FIntVector(0, 165, 255);
return;
}
if (NormalizedFaceId == TEXT("B"))
{
OutColorId = TEXT("blue");
OutNotation = TEXT("B");
OutSampleBgr = FIntVector(255, 0, 0);
return;
}
OutColorId = TEXT("green");
OutNotation = TEXT("F");
OutSampleBgr = FIntVector(0, 255, 0);
}
void ApplyMockClassicFaceToObservation(
FHyperTwistVisionFaceObservation& Observation,
const FString& FaceId
)
{
Observation.FaceId = FaceId;
FString ColorId;
FString Notation;
FIntVector SampleBgr = FIntVector::ZeroValue;
ResolveMockClassicFaceSwatch(FaceId, ColorId, Notation, SampleBgr);
for (FHyperTwistVisionObservedSticker& Sticker : Observation.OrderedStickers)
{
Sticker.ColorId = ColorId;
Sticker.Notation = Notation;
Sticker.SampleBgr = SampleBgr;
}
}
}
bool UHyperTwistMockVisionClient::OpenVisionSession(const FHyperTwistVisionSessionConfig& Config, FString& OutError)
{
OutError.Reset();
@ -26,7 +98,10 @@ FHyperTwistVisionPreviewResult UHyperTwistMockVisionClient::SubmitVisionFrame(co
const int32 NextFrameCount = SessionFrameCounts.FindRef(Frame.SessionId) + 1;
SessionFrameCounts.Add(Frame.SessionId, NextFrameCount);
Result.ObservedFace.FaceId = !Frame.TargetFaceHint.IsEmpty() ? Frame.TargetFaceHint : Result.ObservedFace.FaceId;
HyperTwistMockVisionClientInternal::ApplyMockClassicFaceToObservation(
Result.ObservedFace,
!Frame.TargetFaceHint.IsEmpty() ? Frame.TargetFaceHint : Result.ObservedFace.FaceId
);
Result.ObservedFace.ObservationId = Result.FrameId.IsEmpty()
? TEXT("observation_preview")
: FString::Printf(TEXT("%s/preview"), *Result.FrameId);
@ -43,7 +118,10 @@ FHyperTwistVisionCommitResult UHyperTwistMockVisionClient::CommitVisionObservati
Result.CommitKind = Request.CommitKind;
Result.CommittedUnit = !Request.TargetFace.IsEmpty() ? Request.TargetFace : Request.TargetStage;
Result.Snapshot.State.SourceSessionId = Request.SessionId;
Result.ObservedFace.FaceId = !Request.TargetFace.IsEmpty() ? Request.TargetFace : Result.ObservedFace.FaceId;
HyperTwistMockVisionClientInternal::ApplyMockClassicFaceToObservation(
Result.ObservedFace,
!Request.TargetFace.IsEmpty() ? Request.TargetFace : Result.ObservedFace.FaceId
);
Result.ObservedFace.ObservationId = Request.SessionId.IsEmpty()
? TEXT("observation_commit")
: FString::Printf(TEXT("%s/commit"), *Request.SessionId);

View file

@ -1872,6 +1872,464 @@ namespace HyperTwistTrainingSubsystemInternal
&& SessionConfig.PuzzleId == TEXT("cube/3x3x3");
}
const TArray<FString>& GetClassicRecognitionFaceOrder()
{
static const TArray<FString> FaceOrder = {
TEXT("U"),
TEXT("R"),
TEXT("F"),
TEXT("D"),
TEXT("L"),
TEXT("B")
};
return FaceOrder;
}
FString NormalizeRecognitionFaceId(const FString& FaceId)
{
FString Normalized = FaceId;
Normalized = Normalized.TrimStartAndEnd();
Normalized.ToUpperInline();
return Normalized;
}
int32 GetClassicRecognitionFaceOrderIndex(const FString& FaceId)
{
const FString NormalizedFaceId = NormalizeRecognitionFaceId(FaceId);
const TArray<FString>& FaceOrder = GetClassicRecognitionFaceOrder();
for (int32 Index = 0; Index < FaceOrder.Num(); ++Index)
{
if (FaceOrder[Index] == NormalizedFaceId)
{
return Index;
}
}
return MAX_int32;
}
const FHyperTwistVisionCommittedFaceState* FindCommittedFaceState(
const FHyperTwistVisionReconstructionSession& Session,
const FString& FaceId
)
{
const FString NormalizedFaceId = NormalizeRecognitionFaceId(FaceId);
return Session.CommittedFaces.FindByPredicate(
[&NormalizedFaceId](const FHyperTwistVisionCommittedFaceState& Candidate)
{
return NormalizeRecognitionFaceId(Candidate.FaceId) == NormalizedFaceId;
}
);
}
const FHyperTwistVisionReconstructionStickerVote* FindStickerVote(
const FHyperTwistVisionCommittedFaceState& FaceState,
const int32 GridIndex
)
{
return FaceState.StickerVotes.FindByPredicate(
[GridIndex](const FHyperTwistVisionReconstructionStickerVote& Candidate)
{
return Candidate.GridIndex == GridIndex;
}
);
}
FString MakeStickerVoteKey(const FString& Notation, const FString& ColorId)
{
FString NormalizedColorId = ColorId;
NormalizedColorId.ToLowerInline();
return FString::Printf(
TEXT("%s|%s"),
*NormalizeRecognitionFaceId(Notation),
*NormalizedColorId
);
}
FString ResolveClassicFaceletToken(const FString& Notation, const FString& ColorId)
{
FString NormalizedColorId = ColorId;
NormalizedColorId.ToLowerInline();
if (NormalizedColorId == TEXT("white"))
{
return TEXT("W");
}
if (NormalizedColorId == TEXT("red"))
{
return TEXT("R");
}
if (NormalizedColorId == TEXT("green"))
{
return TEXT("G");
}
if (NormalizedColorId == TEXT("yellow"))
{
return TEXT("Y");
}
if (NormalizedColorId == TEXT("orange"))
{
return TEXT("O");
}
if (NormalizedColorId == TEXT("blue"))
{
return TEXT("B");
}
const FString NormalizedNotation = NormalizeRecognitionFaceId(Notation);
if (NormalizedNotation == TEXT("U"))
{
return TEXT("W");
}
if (NormalizedNotation == TEXT("R"))
{
return TEXT("R");
}
if (NormalizedNotation == TEXT("F"))
{
return TEXT("G");
}
if (NormalizedNotation == TEXT("D"))
{
return TEXT("Y");
}
if (NormalizedNotation == TEXT("L"))
{
return TEXT("O");
}
if (NormalizedNotation == TEXT("B"))
{
return TEXT("B");
}
return FString();
}
bool IsUniformFaceletTokenArray(const TArray<FString>& FaceTokens)
{
if (FaceTokens.Num() != 9 || FaceTokens[0].IsEmpty())
{
return false;
}
for (int32 Index = 1; Index < FaceTokens.Num(); ++Index)
{
if (FaceTokens[Index] != FaceTokens[0])
{
return false;
}
}
return true;
}
TArray<FString>* ResolveMutableClassicFaceTokens(
FHyperTwistClassicFaceletSnapshotState& FaceletState,
const FString& FaceId
)
{
const FString NormalizedFaceId = NormalizeRecognitionFaceId(FaceId);
if (NormalizedFaceId == TEXT("U"))
{
return &FaceletState.U;
}
if (NormalizedFaceId == TEXT("R"))
{
return &FaceletState.R;
}
if (NormalizedFaceId == TEXT("F"))
{
return &FaceletState.F;
}
if (NormalizedFaceId == TEXT("D"))
{
return &FaceletState.D;
}
if (NormalizedFaceId == TEXT("L"))
{
return &FaceletState.L;
}
if (NormalizedFaceId == TEXT("B"))
{
return &FaceletState.B;
}
return nullptr;
}
FHyperTwistVisionReconstructionSession BuildClassicRecognitionReconstructionSession(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FString& SessionId,
const FHyperTwistVisionReconstructionSession& ExistingSession,
const FHyperTwistVisionFaceObservation& Observation,
bool& OutWasRevision
)
{
OutWasRevision = false;
FHyperTwistVisionReconstructionSession Session = ExistingSession.IsStructurallyValid()
? ExistingSession
: FHyperTwistVisionReconstructionSession();
Session.SessionId = SessionId;
Session.PuzzleId = !SessionConfig.PuzzleId.IsEmpty() ? SessionConfig.PuzzleId : TEXT("cube/3x3x3");
Session.ReconstructionProfile = TEXT("classic-face-vote-v1");
Session.OrientationProfile = TEXT("classic-target-face-net-v1");
Session.bUsesObservationVoting = true;
const FString FaceId = NormalizeRecognitionFaceId(Observation.FaceId);
const FHyperTwistVisionCommittedFaceState* PreviousFaceState = FindCommittedFaceState(Session, FaceId);
OutWasRevision = PreviousFaceState != nullptr;
FHyperTwistVisionCommittedFaceState FaceState;
FaceState.FaceId = FaceId;
FaceState.LatestObservationId = Observation.ObservationId;
FaceState.OrderingProfile = Observation.OrderingProfile;
FaceState.ObservationCount = PreviousFaceState != nullptr
? PreviousFaceState->ObservationCount + 1
: 1;
FaceState.RevisionCount = PreviousFaceState != nullptr
? PreviousFaceState->RevisionCount + 1
: 0;
FaceState.LatestOrderedStickers = Observation.OrderedStickers;
for (const FHyperTwistVisionObservedSticker& Sticker : Observation.OrderedStickers)
{
TArray<FHyperTwistVisionStickerVoteTally> Tallies;
if (PreviousFaceState != nullptr)
{
if (const FHyperTwistVisionReconstructionStickerVote* PreviousVote =
FindStickerVote(*PreviousFaceState, Sticker.GridIndex))
{
Tallies = PreviousVote->Tallies;
}
}
const FString StickerVoteKey = MakeStickerVoteKey(Sticker.Notation, Sticker.ColorId);
bool bUpdatedExistingTally = false;
for (FHyperTwistVisionStickerVoteTally& Tally : Tallies)
{
if (MakeStickerVoteKey(Tally.Notation, Tally.ColorId) == StickerVoteKey)
{
++Tally.VoteCount;
bUpdatedExistingTally = true;
break;
}
}
if (!bUpdatedExistingTally)
{
FHyperTwistVisionStickerVoteTally Tally;
Tally.Notation = NormalizeRecognitionFaceId(Sticker.Notation);
Tally.ColorId = Sticker.ColorId;
Tally.VoteCount = 1;
Tallies.Add(Tally);
}
int32 WinningIndex = INDEX_NONE;
int32 TotalVoteCount = 0;
for (int32 TallyIndex = 0; TallyIndex < Tallies.Num(); ++TallyIndex)
{
const FHyperTwistVisionStickerVoteTally& Tally = Tallies[TallyIndex];
TotalVoteCount += Tally.VoteCount;
const bool bPreferThisTally = WinningIndex == INDEX_NONE
|| Tally.VoteCount > Tallies[WinningIndex].VoteCount
|| (Tally.VoteCount == Tallies[WinningIndex].VoteCount
&& MakeStickerVoteKey(Tally.Notation, Tally.ColorId) == StickerVoteKey);
if (bPreferThisTally)
{
WinningIndex = TallyIndex;
}
}
if (WinningIndex == INDEX_NONE)
{
continue;
}
const FHyperTwistVisionStickerVoteTally& WinningTally = Tallies[WinningIndex];
FHyperTwistVisionReconstructionStickerVote Vote;
Vote.GridIndex = Sticker.GridIndex;
Vote.GridRow = Sticker.GridRow;
Vote.GridColumn = Sticker.GridColumn;
Vote.WinningNotation = WinningTally.Notation;
Vote.WinningColorId = WinningTally.ColorId;
Vote.WinningVoteCount = WinningTally.VoteCount;
Vote.TotalVoteCount = TotalVoteCount;
Vote.bHadConflict = Tallies.Num() > 1;
Vote.Tallies = Tallies;
FaceState.bHasConflicts = FaceState.bHasConflicts || Vote.bHadConflict;
FaceState.StickerVotes.Add(Vote);
}
const int32 ExistingFaceIndex = Session.CommittedFaces.IndexOfByPredicate(
[&FaceId](const FHyperTwistVisionCommittedFaceState& Candidate)
{
return NormalizeRecognitionFaceId(Candidate.FaceId) == FaceId;
}
);
if (ExistingFaceIndex == INDEX_NONE)
{
Session.CommittedFaces.Add(FaceState);
}
else
{
Session.CommittedFaces[ExistingFaceIndex] = FaceState;
}
Session.CommittedFaces.Sort(
[](const FHyperTwistVisionCommittedFaceState& Left, const FHyperTwistVisionCommittedFaceState& Right)
{
return GetClassicRecognitionFaceOrderIndex(Left.FaceId)
< GetClassicRecognitionFaceOrderIndex(Right.FaceId);
}
);
Session.TotalCommittedFaceCount = Session.CommittedFaces.Num();
Session.TotalRevisionCount = 0;
float ConfidenceSum = 0.0f;
int32 ConfidenceCount = 0;
for (const FHyperTwistVisionCommittedFaceState& CommittedFace : Session.CommittedFaces)
{
Session.TotalRevisionCount += CommittedFace.RevisionCount;
for (const FHyperTwistVisionObservedSticker& LatestSticker : CommittedFace.LatestOrderedStickers)
{
ConfidenceSum += LatestSticker.Confidence;
++ConfidenceCount;
}
}
Session.ConfidenceRollup = ConfidenceCount > 0
? ConfidenceSum / static_cast<float>(ConfidenceCount)
: 0.0f;
Session.bHasStableOrientation = Session.TotalCommittedFaceCount > 0;
Session.OrientationStateOrdinal = Session.TotalCommittedFaceCount;
Session.MissingFaces.Reset();
for (const FString& ExpectedFaceId : GetClassicRecognitionFaceOrder())
{
if (FindCommittedFaceState(Session, ExpectedFaceId) == nullptr)
{
Session.MissingFaces.Add(ExpectedFaceId);
}
}
Session.bHasCompleteClassicCubeNet =
Session.MissingFaces.Num() == 0
&& Session.CommittedFaces.Num() == GetClassicRecognitionFaceOrder().Num();
return Session;
}
bool TryBuildClassicFaceletSnapshotFromReconstruction(
const FHyperTwistVisionReconstructionSession& Session,
FHyperTwistClassicFaceletSnapshotState& OutFaceletState
)
{
if (!Session.IsStructurallyValid() || !Session.bHasCompleteClassicCubeNet)
{
return false;
}
OutFaceletState = FHyperTwistClassicFaceletSnapshotState();
OutFaceletState.bPreviewState = false;
for (const FString& ExpectedFaceId : GetClassicRecognitionFaceOrder())
{
const FHyperTwistVisionCommittedFaceState* CommittedFace = FindCommittedFaceState(Session, ExpectedFaceId);
TArray<FString>* FaceTokens = ResolveMutableClassicFaceTokens(OutFaceletState, ExpectedFaceId);
if (CommittedFace == nullptr || FaceTokens == nullptr || CommittedFace->StickerVotes.Num() != 9)
{
return false;
}
FaceTokens->Reset();
for (int32 GridIndex = 0; GridIndex < 9; ++GridIndex)
{
const FHyperTwistVisionReconstructionStickerVote* Vote = FindStickerVote(*CommittedFace, GridIndex);
if (Vote == nullptr)
{
return false;
}
const FString Token = ResolveClassicFaceletToken(Vote->WinningNotation, Vote->WinningColorId);
if (Token.IsEmpty())
{
return false;
}
FaceTokens->Add(Token);
}
}
return OutFaceletState.U.Num() == 9
&& OutFaceletState.R.Num() == 9
&& OutFaceletState.F.Num() == 9
&& OutFaceletState.D.Num() == 9
&& OutFaceletState.L.Num() == 9
&& OutFaceletState.B.Num() == 9;
}
bool TryBuildClassicRecognitionStateSnapshot(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& Session,
FHyperTwistPuzzleState& OutState,
FHyperTwistStateSnapshot& OutSnapshot
)
{
FHyperTwistClassicFaceletSnapshotState FaceletState;
if (!TryBuildClassicFaceletSnapshotFromReconstruction(Session, FaceletState))
{
return false;
}
FString PayloadJson;
if (!SerializeStruct(FaceletState, PayloadJson))
{
return false;
}
FHyperTwistPuzzleDefinitionRef Definition;
Definition.PuzzleId = !Session.PuzzleId.IsEmpty() ? Session.PuzzleId : TEXT("cube/3x3x3");
Definition.PuzzleFamily = EHyperTwistPuzzleFamily::ClassicCube;
Definition.Dimension = 3;
Definition.DefinitionVersion = TEXT("2026.05");
Definition.NotationProfile = !SessionConfig.NotationProfile.IsEmpty()
? SessionConfig.NotationProfile
: TEXT("classic-wca");
Definition.SizeVector = {3, 3, 3};
OutState = FHyperTwistPuzzleState();
OutState.Definition = Definition;
OutState.StateEncodingKind = EHyperTwistStateEncodingKind::Facelet;
OutState.StateEncoding.EncodingProfile = FaceletState.EncodingProfile;
OutState.StateEncoding.PayloadJson = PayloadJson;
OutState.OrientationFrame.Reference = !Session.OrientationProfile.IsEmpty()
? Session.OrientationProfile
: TEXT("classic-target-face-net-v1");
OutState.bIsSolved =
IsUniformFaceletTokenArray(FaceletState.U)
&& IsUniformFaceletTokenArray(FaceletState.R)
&& IsUniformFaceletTokenArray(FaceletState.F)
&& IsUniformFaceletTokenArray(FaceletState.D)
&& IsUniformFaceletTokenArray(FaceletState.L)
&& IsUniformFaceletTokenArray(FaceletState.B);
OutState.Source = EHyperTwistStateSource::Recognition;
OutState.CapturedAtUtc = FDateTime::UtcNow().ToIso8601();
OutState.SourceConfidence = Session.ConfidenceRollup;
OutState.SourceSessionId = Session.SessionId;
OutState.Notes = TEXT("Phase 6R-C reconstruction session aggregate.");
OutSnapshot = FHyperTwistStateSnapshot();
OutSnapshot.SnapshotId = FString::Printf(TEXT("recognition_reconstruction_%s"), *Session.SessionId);
OutSnapshot.State = OutState;
OutSnapshot.DerivedHash = FString::Printf(
TEXT("recognition-reconstruction-%s-%d-%d"),
*Session.SessionId,
Session.TotalCommittedFaceCount,
Session.TotalRevisionCount
);
return OutSnapshot.IsStructurallyValid();
}
FHyperTwistVisionCalibrationProfile BuildDefaultClassicCubeCalibrationProfile(const FString& ProfileId)
{
FHyperTwistVisionCalibrationProfile Profile;
@ -6078,6 +6536,7 @@ bool UHyperTwistTrainingSubsystem::OpenActiveRecognitionSession(FString& OutErro
ActiveRecognitionSessionState.SessionConfig = SessionConfig;
ActiveRecognitionSessionState.bHasPreviewResult = false;
ActiveRecognitionSessionState.bHasCommitResult = false;
ActiveRecognitionSessionState.ActiveReconstructionSession = FHyperTwistVisionReconstructionSession();
ActiveRecognitionSessionState.LastPreviewResult = FHyperTwistVisionPreviewResult();
ActiveRecognitionSessionState.LastCommitResult = FHyperTwistVisionCommitResult();
RefreshRecognitionServiceHealth();
@ -6231,16 +6690,70 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit
Result.Snapshot.State.SourceSessionId = NormalizedRequest.SessionId;
}
bool bWasReconstructionRevision = false;
if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig)
&& Result.ObservedFace.IsStructurallyValid())
{
if (Result.CommittedUnit.IsEmpty())
{
Result.CommittedUnit = HyperTwistTrainingSubsystemInternal::NormalizeRecognitionFaceId(
Result.ObservedFace.FaceId
);
}
Result.ReconstructionSession =
HyperTwistTrainingSubsystemInternal::BuildClassicRecognitionReconstructionSession(
ActiveRecognitionSessionState.SessionConfig,
Result.SessionId,
ActiveRecognitionSessionState.ActiveReconstructionSession,
Result.ObservedFace,
bWasReconstructionRevision
);
ActiveRecognitionSessionState.ActiveReconstructionSession = Result.ReconstructionSession;
TArray<FString> ProviderMissingUnits = Result.MissingUnits;
Result.MissingUnits = Result.ReconstructionSession.MissingFaces;
for (const FString& ProviderMissingUnit : ProviderMissingUnits)
{
Result.MissingUnits.AddUnique(ProviderMissingUnit);
}
Result.Confidence = Result.ReconstructionSession.ConfidenceRollup;
if (Result.ReconstructionSession.bHasCompleteClassicCubeNet)
{
FHyperTwistPuzzleState ReconstructedState;
FHyperTwistStateSnapshot ReconstructedSnapshot;
if (HyperTwistTrainingSubsystemInternal::TryBuildClassicRecognitionStateSnapshot(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
ReconstructedState,
ReconstructedSnapshot
))
{
Result.Snapshot = ReconstructedSnapshot;
}
else
{
Result.Snapshot = FHyperTwistStateSnapshot();
}
}
else
{
Result.Snapshot = FHyperTwistStateSnapshot();
}
}
const FString ResolvedStage = !Result.CommittedUnit.IsEmpty()
? Result.CommittedUnit
: (!NormalizedRequest.TargetStage.IsEmpty() ? NormalizedRequest.TargetStage : NormalizedRequest.TargetFace);
const bool bHasCorrectionSignals =
Result.Warnings.Num() > 0 || Result.MissingUnits.Num() > 0;
Result.Warnings.Num() > 0 || bWasReconstructionRevision;
const bool bHasCommitSignal =
!Result.CommittedUnit.IsEmpty()
|| Result.Snapshot.IsStructurallyValid()
|| Result.Snapshot.State.IsStructurallyValid()
|| Result.Confidence > 0.0f;
|| Result.Confidence > 0.0f
|| Result.ReconstructionSession.IsStructurallyValid();
if (bHasCorrectionSignals)
{
@ -6327,6 +6840,48 @@ FHyperTwistVisionFinalizeResult UHyperTwistTrainingSubsystem::FinalizeActiveReco
Result.FinalSnapshot.State.SourceSessionId = SessionId;
}
if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig))
{
Result.ReconstructionSession = ActiveRecognitionSessionState.ActiveReconstructionSession;
if (Result.ReconstructionSession.IsStructurallyValid())
{
Result.ConfidenceRollup = Result.ReconstructionSession.ConfidenceRollup;
}
if (Result.ReconstructionSession.IsStructurallyValid()
&& Result.ReconstructionSession.bHasCompleteClassicCubeNet)
{
FHyperTwistPuzzleState ReconstructedState;
FHyperTwistStateSnapshot ReconstructedSnapshot;
if (HyperTwistTrainingSubsystemInternal::TryBuildClassicRecognitionStateSnapshot(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
ReconstructedState,
ReconstructedSnapshot
))
{
Result.FinalState = ReconstructedState;
Result.FinalSnapshot = ReconstructedSnapshot;
}
else
{
Result.FinalState = FHyperTwistPuzzleState();
Result.FinalSnapshot = FHyperTwistStateSnapshot();
Result.NormalizationWarnings.AddUnique(TEXT("recognition-reconstruction-unavailable"));
}
}
else
{
Result.FinalState = FHyperTwistPuzzleState();
Result.FinalSnapshot = FHyperTwistStateSnapshot();
Result.NormalizationWarnings.AddUnique(
Result.ReconstructionSession.IsStructurallyValid()
? TEXT("recognition-reconstruction-incomplete")
: TEXT("recognition-reconstruction-unavailable")
);
}
}
TArray<FHyperTwistReplayEvent> PendingRecognitionEvents;
for (const FString& SeedJson : Result.ReplaySeedEventsJson)
{
@ -6433,6 +6988,7 @@ FHyperTwistVisionFinalizeResult UHyperTwistTrainingSubsystem::FinalizeActiveReco
ActiveRecognitionSessionState.bSessionOpen = false;
ActiveRecognitionSessionState.bHasFinalizeResult = true;
ActiveRecognitionSessionState.LastFinalizeResult = Result;
ActiveRecognitionSessionState.ActiveReconstructionSession = FHyperTwistVisionReconstructionSession();
ActiveRecognitionSessionState.ActiveSessionId.Reset();
ActiveRecognitionSessionState.LastUpdatedAtUtc = FDateTime::UtcNow().ToIso8601();
++ActiveRecognitionSessionState.FinalizedSessionCount;
@ -6480,6 +7036,7 @@ bool UHyperTwistTrainingSubsystem::CloseActiveRecognitionSession(FString& OutErr
ActiveRecognitionSessionState.bSessionOpen = false;
ActiveRecognitionSessionState.ActiveSessionId.Reset();
ActiveRecognitionSessionState.ActiveReconstructionSession = FHyperTwistVisionReconstructionSession();
ActiveRecognitionSessionState.LastUpdatedAtUtc = FDateTime::UtcNow().ToIso8601();
ActiveRecognitionSessionState.LastError.Reset();
RefreshRecognitionServiceHealth();

View file

@ -173,6 +173,208 @@ struct FHyperTwistVisionFaceObservation
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionStickerVoteTally
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Notation;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ColorId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 VoteCount = 0;
bool IsStructurallyValid() const
{
return !Notation.IsEmpty() && !ColorId.IsEmpty() && VoteCount > 0;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionReconstructionStickerVote
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 GridIndex = -1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 GridRow = -1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 GridColumn = -1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString WinningNotation;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString WinningColorId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 WinningVoteCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 TotalVoteCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHadConflict = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionStickerVoteTally> Tallies;
bool IsStructurallyValid() const
{
if (GridIndex < 0
|| GridRow < 0
|| GridColumn < 0
|| WinningNotation.IsEmpty()
|| WinningColorId.IsEmpty()
|| WinningVoteCount <= 0
|| TotalVoteCount <= 0
|| Tallies.Num() <= 0)
{
return false;
}
for (const FHyperTwistVisionStickerVoteTally& Tally : Tallies)
{
if (!Tally.IsStructurallyValid())
{
return false;
}
}
return true;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionCommittedFaceState
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FaceId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString LatestObservationId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString OrderingProfile = TEXT("3x3-row-major");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ObservationCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 RevisionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasConflicts = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionObservedSticker> LatestOrderedStickers;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionReconstructionStickerVote> StickerVotes;
bool IsStructurallyValid() const
{
if (FaceId.IsEmpty()
|| LatestObservationId.IsEmpty()
|| LatestOrderedStickers.Num() != 9
|| StickerVotes.Num() != 9
|| ObservationCount <= 0
|| RevisionCount < 0)
{
return false;
}
for (const FHyperTwistVisionObservedSticker& Sticker : LatestOrderedStickers)
{
if (!Sticker.IsStructurallyValid())
{
return false;
}
}
for (const FHyperTwistVisionReconstructionStickerVote& Vote : StickerVotes)
{
if (!Vote.IsStructurallyValid())
{
return false;
}
}
return true;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionReconstructionSession
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SessionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PuzzleId = TEXT("cube/3x3x3");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ReconstructionProfile = TEXT("classic-face-vote-v1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString OrientationProfile = TEXT("classic-target-face-net-v1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bUsesObservationVoting = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasStableOrientation = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OrientationStateOrdinal = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 TotalCommittedFaceCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 TotalRevisionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
float ConfidenceRollup = 0.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasCompleteClassicCubeNet = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionCommittedFaceState> CommittedFaces;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> MissingFaces;
bool IsStructurallyValid() const
{
if (SessionId.IsEmpty() || PuzzleId.IsEmpty() || ReconstructionProfile.IsEmpty() || OrientationProfile.IsEmpty())
{
return false;
}
for (const FHyperTwistVisionCommittedFaceState& Face : CommittedFaces)
{
if (!Face.IsStructurallyValid())
{
return false;
}
}
return TotalCommittedFaceCount >= 0 && TotalRevisionCount >= 0;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionSessionConfig
{
@ -310,6 +512,9 @@ struct FHyperTwistVisionCommitResult
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionFaceObservation ObservedFace;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionReconstructionSession ReconstructionSession;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> Warnings;
@ -331,6 +536,9 @@ struct FHyperTwistVisionFinalizeResult
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistStateSnapshot FinalSnapshot;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionReconstructionSession ReconstructionSession;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> ReplaySeedEventsJson;

View file

@ -1885,6 +1885,9 @@ struct FHyperTwistTrainingRecognitionSessionState
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionFinalizeResult LastFinalizeResult;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionReconstructionSession ActiveReconstructionSession;
};
USTRUCT(BlueprintType)

View file

@ -0,0 +1,245 @@
// Copyright HyperTwist, Inc. All Rights Reserved.
#include "Misc/AutomationTest.h"
#include "Engine/GameInstance.h"
#include "HyperTwistCore/HyperTwistCoreLibrary.h"
#include "HyperTwistCore/HyperTwistCoreTypes.h"
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
#include "JsonObjectConverter.h"
#include "UObject/UnrealType.h"
#if WITH_AUTOMATION_TESTS
namespace HyperTwistRubixCubeSolverPhase6RCTestInternal
{
FHyperTwistTrainingDeck MakeRecognitionDeck()
{
FHyperTwistTrainingDeck Deck;
Deck.DeckId = TEXT("phase6r-c/rubix-cube-solver-recognition");
Deck.Title = TEXT("Phase 6R-C Rubix Cube Solver Recognition");
Deck.DeliveryModes = {
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
EHyperTwistTrainingDeliveryMode::CoachReviewed
};
FHyperTwistTrainingCase TrainingCase;
TrainingCase.CaseId = TEXT("phase6r-c-case");
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition;
TrainingCase.PromptLabel = TEXT("Phase 6R-C 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-c-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);
}
const FHyperTwistVisionCommittedFaceState* FindCommittedFace(
const FHyperTwistVisionReconstructionSession& Session,
const FString& FaceId
)
{
return Session.CommittedFaces.FindByPredicate(
[&FaceId](const FHyperTwistVisionCommittedFaceState& Candidate)
{
return Candidate.FaceId.Equals(FaceId, ESearchCase::IgnoreCase);
}
);
}
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RCCommitBuildsReconstructionSessionTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.C.CommitBuildsReconstructionSession",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RCCommitBuildsReconstructionSessionTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RCTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-c-commit-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-C."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
const FHyperTwistVisionCommitResult Result =
HyperTwistRubixCubeSolverPhase6RCTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 1);
TestTrue(TEXT("The commit result must carry a structurally valid reconstruction session."), Result.ReconstructionSession.IsStructurallyValid());
TestEqual(TEXT("The first face commit must keep one committed face."), Result.ReconstructionSession.CommittedFaces.Num(), 1);
TestEqual(TEXT("The first face commit must report five missing faces."), Result.ReconstructionSession.MissingFaces.Num(), 5);
TestEqual(TEXT("The reconstruction session must keep the committed face id."), Result.ReconstructionSession.CommittedFaces[0].FaceId, TEXT("F"));
TestFalse(TEXT("An incomplete reconstruction session must not expose a full snapshot yet."), Result.Snapshot.IsStructurallyValid());
const FHyperTwistTrainingRecognitionSessionState SessionState =
TrainingSubsystem->GetActiveRecognitionSessionState();
TestTrue(TEXT("The active recognition state must keep the same reconstruction session."), SessionState.ActiveReconstructionSession.IsStructurallyValid());
TestEqual(TEXT("The active recognition state must track one committed face."), SessionState.ActiveReconstructionSession.CommittedFaces.Num(), 1);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RCRevisionLedgerTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.C.RevisionLedger",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RCRevisionLedgerTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RCTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-c-revision-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-C."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
HyperTwistRubixCubeSolverPhase6RCTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 1);
const FHyperTwistVisionCommitResult RevisionResult =
HyperTwistRubixCubeSolverPhase6RCTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 2);
const FHyperTwistVisionCommittedFaceState* FaceState =
HyperTwistRubixCubeSolverPhase6RCTestInternal::FindCommittedFace(RevisionResult.ReconstructionSession, TEXT("F"));
TestNotNull(TEXT("The revised reconstruction session must still track the front face."), FaceState);
if (FaceState == nullptr)
{
return false;
}
TestEqual(TEXT("A second face commit must increment the observation count."), FaceState->ObservationCount, 2);
TestEqual(TEXT("A second face commit must increment the revision count."), FaceState->RevisionCount, 1);
TestEqual(TEXT("The revision ledger must preserve one total revision in the session."), RevisionResult.ReconstructionSession.TotalRevisionCount, 1);
TestEqual(TEXT("The first sticker vote must record two votes after a recommit."), FaceState->StickerVotes[0].WinningVoteCount, 2);
TestFalse(TEXT("A revised but incomplete reconstruction session must still not expose a full snapshot."), RevisionResult.Snapshot.IsStructurallyValid());
const FHyperTwistTrainingRecognitionSessionState SessionState =
TrainingSubsystem->GetActiveRecognitionSessionState();
TestEqual(TEXT("A recommit must count as one correction event."), SessionState.CorrectionEventCount, 1);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RCFinalizeBuildsClassicNetTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.C.FinalizeBuildsClassicNet",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RCFinalizeBuildsClassicNetTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RCTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-c-finalize-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-C."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
const TArray<FString> FaceOrder = {
TEXT("U"),
TEXT("R"),
TEXT("F"),
TEXT("D"),
TEXT("L"),
TEXT("B")
};
for (int32 FaceOrdinal = 0; FaceOrdinal < FaceOrder.Num(); ++FaceOrdinal)
{
HyperTwistRubixCubeSolverPhase6RCTestInternal::CommitFace(
TrainingSubsystem,
FaceOrder[FaceOrdinal],
FaceOrdinal + 1
);
}
const FHyperTwistVisionFinalizeResult Result = TrainingSubsystem->FinalizeActiveRecognitionSession();
TestTrue(TEXT("The finalized session must carry a structurally valid reconstruction session."), Result.ReconstructionSession.IsStructurallyValid());
TestTrue(TEXT("The finalized session must carry a complete classic cube net."), Result.ReconstructionSession.bHasCompleteClassicCubeNet);
TestTrue(TEXT("A complete reconstruction session must emit a structurally valid final state."), Result.FinalState.IsStructurallyValid());
TestTrue(TEXT("A complete reconstruction session must emit a structurally valid final snapshot."), Result.FinalSnapshot.IsStructurallyValid());
TestTrue(TEXT("The reconstructed final state should resolve as solved for the mock color net."), UHyperTwistCoreLibrary::IsSolved(Result.FinalState));
TestTrue(TEXT("The bounded finalize path should not need normalization warnings for a complete mock route."), Result.NormalizationWarnings.IsEmpty());
FHyperTwistClassicFaceletSnapshotState FaceletState;
TestTrue(
TEXT("The final facelet payload must deserialize."),
FJsonObjectConverter::JsonObjectStringToUStruct(Result.FinalState.StateEncoding.PayloadJson, &FaceletState, 0, 0)
);
TestEqual(TEXT("The up face must keep nine stickers."), FaceletState.U.Num(), 9);
TestEqual(TEXT("The right face must keep nine stickers."), FaceletState.R.Num(), 9);
TestEqual(TEXT("The front face must keep the green face token."), FaceletState.F[0], TEXT("G"));
TestEqual(TEXT("The back face must keep the blue face token."), FaceletState.B[0], TEXT("B"));
const FHyperTwistTrainingRecognitionSessionState SessionState =
TrainingSubsystem->GetActiveRecognitionSessionState();
TestFalse(TEXT("The active recognition session must be closed after finalization."), SessionState.bSessionOpen);
TestTrue(TEXT("The last finalize result must keep the complete reconstruction session."), SessionState.LastFinalizeResult.ReconstructionSession.bHasCompleteClassicCubeNet);
return true;
}
#endif

View file

@ -94,7 +94,7 @@ Status update on `2026-05-20`:
- the current canonical repo-row portfolio count is `75`
- all `75` current portfolio rows now have explicit root-tracker coverage
- the current implemented-row count is `26`
- the current implemented-row count is `27`
- the four later restrictive clean-room rows now implemented after the original `2026-05-13` truth
block are:
- `cubing/alg.js`
@ -105,6 +105,8 @@ Status update on `2026-05-20`:
- `HactarCE/Hyperspeedcube`
- the sixth additional implemented row is the same-day bounded permissive partial row:
- `kkoomen/qbr`
- the seventh additional implemented row is the same-day bounded permissive partial row:
- `vivaansinghvi07/rubix-cube-solver`
- the earlier first-party packet block is now confirmed landed in current code through:
- `d4f4ad3` `Add primary coach orchestration entry lane`
- `1860f14` `Add provider-backed recognition sidecar client`
@ -114,8 +116,11 @@ Status update on `2026-05-20`:
- `HactarCE/Hyperspeedcube` remains partially incorporated
- the bounded permissive `Phase 6R-B` `kkoomen/qbr` classic-cube recognition calibration and
ordered face-observation packet is now landed in current code
- `kkoomen/qbr` remains partially incorporated; the current next bounded move is a source-backed
`Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` preparation/control pass, not a new
- `kkoomen/qbr` remains partially incorporated
- the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face
reconstruction and face-vote replacement packet is now landed in current code
- `vivaansinghvi07/rubix-cube-solver` remains partially incorporated; the current next bounded
move is a source-backed `Phase 6R-D` `roice3/MagicTile` 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
@ -123,16 +128,17 @@ Status update on `2026-05-20`:
This file now also preserves the current truth that future models must not lose:
- current curated HyperTwist shallow-eval set: `75` repos
- currently verified live/implemented in checked `UnrealHyperTwist` surfaces: `26`
- permissive live lanes: `15`
- currently verified live/implemented in checked `UnrealHyperTwist` surfaces: `27`
- permissive live lanes: `16`
- boundary-sensitive live lanes: `6`
- restrictive live lanes: `5`
The fifteen permissive live lanes are:
The sixteen permissive live lanes are:
- `Aarav2709/KubeTimr`
- `HactarCE/Hyperspeedcube`
- `kkoomen/qbr`
- `vivaansinghvi07/rubix-cube-solver`
- `apache/echarts`
- `abunickabhi/5style-Trainer`
- `google/model-viewer`
@ -215,8 +221,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-C`
`vivaansinghvi07/rubix-cube-solver` preparation/control pass
- the next bounded move is a source-backed `Phase 6R-D` `roice3/MagicTile`
preparation/control pass
Companion docs:
@ -748,9 +754,18 @@ Approved working posture:
- HyperTwist may use the checked repo directly under the upstream `MIT` posture
- preserve the upstream `MIT` license text and copyright notice:
- `Copyright (c) 2023 Vivaan Singhvi`
- keep the row queued as the recognition companion candidate after `kkoomen/qbr`
- 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:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- 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
### `tentone/rubix-solver`
@ -1258,10 +1273,10 @@ Approved working posture:
permission to transplant the whole Rust application shell
- do not let this row absorb non-Euclidean topology ownership that remains queued for
`roice3/MagicTile`
- after the landed puzzle-catalog slice and the landed bounded `qbr` calibration slice, the next
queue head should move to a source-backed `Phase 6R-C`
`vivaansinghvi07/rubix-cube-solver` preparation/control pass rather than pretending the entire
retained recognition family is already closed
- after the landed puzzle-catalog slice, the landed bounded `qbr` calibration slice, and the
landed bounded `rubix-cube-solver` reconstruction slice, the next queue head should move to a
source-backed `Phase 6R-D` `roice3/MagicTile` preparation/control pass rather than pretending
the entire retained topology family is already closed
### `coqui-ai/TTS`

View file

@ -0,0 +1,134 @@
# HyperTwist Phase 6R-C rubix-cube-solver reconstruction implementation packet
Created on `2026-05-20`
## Status
- first-party HyperTwist packet
- bounded permissive `Phase 6R-C` implementation slice
## Purpose
This packet lands the first narrower bounded slice from the retained
`vivaansinghvi07/rubix-cube-solver` recognition-companion row.
The landed slice is:
- first-party classic-cube committed-face reconstruction session plus face-vote replacement ledger
It is not:
- a full `rubix-cube-solver` row transplant
- a browser/webcam shell packet
- a broad solve-explanation packet
- a bundled `twistysim.min.js` redistribution packet
- a `MagicTile` topology packet
## Current authority basis
This implementation packet stands on:
- `docs/HYPERTWIST_PHASE_0R_PACKET_0R_A_EVALUATION_2026-05-12.md`
- `docs/HYPERTWIST_PHASE_2R_PACKET_2R_A_OWNERSHIP_AND_ACCEPTANCE_CONTRACT_2026-05-13.md`
- `docs/REPO_LICENSE_TRACKING.md`
- `docs/arch/HYPERTWIST_PHASE6R_C_RUBIX_CUBE_SOLVER_RECONSTRUCTION_PREPARATION_PACKET_2026-05-20.md`
The retained owner remains:
- `vivaansinghvi07/rubix-cube-solver`
The granted family remains bounded to:
- committed-face reconstruction flow
- face-vote replacement and correction ledger
- bounded final net shaping above committed faces
This packet lands only the first narrower family in that granted set.
## Landed scope
The current code now owns a retained classic-cube reconstruction contract through:
- retained recognition contract types for:
- sticker vote tallies
- reconstruction sticker votes
- committed face state
- reconstruction session
- recognition session state tracking for the active reconstruction session
- classic-cube reconstruction-session aggregation and face-vote replacement in
`UHyperTwistTrainingSubsystem`
- classic-cube missing-face accounting and confidence rollup derived from the bounded
reconstruction session
- final classic facelet snapshot and net shaping when the six-face reconstruction becomes complete
- sample reconstruction-session routing in `UHyperTwistContractLibrary`
- ordered classic-face preview and commit payload correction in `UHyperTwistMockVisionClient`
- focused automation coverage in:
- `HyperTwistRubixCubeSolverPhase6RCReconstructionContractTest.cpp`
## Why this is still intentionally bounded
This packet replaces the thinner commit/finalize reconstruction heuristics with a reconstruction
session owned by first-party code, but it does not widen into the neighboring retained families.
Still deferred:
- browser/webcam shell
- broad solve explanation or recommendation shell
- bundled `twistysim.min.js` redistribution
- generalized solver backend ownership
- `MagicTile` or broader topology widening
## Validation
Build validation:
- `Build.bat UnrealHyperTwistEditor Win64 Development -Project='C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject' -WaitMutex -NoHotReloadFromIDE`
Focused automation validation:
- `Automation RunTests HyperTwist.Permissive.RubixCubeSolver.Phase6R.C`
Regression automation validation:
- `Automation RunTests HyperTwist.Permissive.Qbr.Phase6R.B`
- `Automation RunTests HyperTwist.Permissive.Hyperspeedcube.Phase6R.A`
- `Automation RunTests HyperTwist.CleanRoom.CubeDesk`
Expected covered tests:
- `CommitBuildsReconstructionSession`
- `RevisionLedger`
- `FinalizeBuildsClassicNet`
- `CalibrationSessionConfig`
- `PreviewObservation`
- `CommitObservation`
- `CatalogLookup`
- `SampleDefinition`
- `TrainingRunDefinition`
- existing `CubeDesk` clean-room regression suite
## Queue effect
This packet consumes the current `Phase 6R-C` implementation slice.
`vivaansinghvi07/rubix-cube-solver` remains only partially incorporated:
- landed now:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- still deferred:
- browser/webcam shell
- broad solve explanation or recommendation shell
- bundled `twistysim.min.js` redistribution
The next clean move is not another immediate `rubix-cube-solver` widening by default.
The next queue head should be:
- a source-backed `Phase 6R-D` `roice3/MagicTile` preparation/control pass
Keep the future legal sequencing guard visible when this row widens again:
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing its
upstream provenance/license

View file

@ -0,0 +1,184 @@
# HyperTwist Phase 6R-C rubix-cube-solver reconstruction preparation packet
Created on `2026-05-20`
## Status
- historical same-day preparation authority
- bounded post-Phase-`6R-B` preparation slice
- the first bounded implementation slice now lands separately under:
- `docs/arch/HYPERTWIST_PHASE6R_C_RUBIX_CUBE_SOLVER_RECONSTRUCTION_IMPLEMENTATION_PACKET_2026-05-20.md`
## Purpose
This packet freezes the next widening order after the landed `Phase 6R-B`
`kkoomen/qbr` classic-cube recognition calibration and ordered face-observation packet.
The open task is:
- define the first bounded source-backed widening packet for
`vivaansinghvi07/rubix-cube-solver` as the retained `Phase 6R-C` reconstruction companion lane
It is not:
- a full `rubix-cube-solver` row transplant
- a browser/webcam shell packet
- a broad solve-explanation packet
- a direct redistribution packet for the bundled `frontend/lib/twistysim.min.js` asset
- a `MagicTile` topology packet
- a general recognition-provider widening packet
## Current authority basis
This preparation packet stands on already-closed authority:
- `docs/HYPERTWIST_PHASE_0R_PACKET_0R_A_EVALUATION_2026-05-12.md`
- `docs/HYPERTWIST_PHASE_2R_PACKET_2R_A_OWNERSHIP_AND_ACCEPTANCE_CONTRACT_2026-05-13.md`
- `docs/REPO_LICENSE_TRACKING.md`
- `docs/arch/HYPERTWIST_PHASE6R_B_QBR_CALIBRATED_FACE_OBSERVATION_IMPLEMENTATION_PACKET_2026-05-20.md`
The key accepted routing facts are:
- `vivaansinghvi07/rubix-cube-solver` is the retained recognition companion donor immediately after
the landed `6R-B` slice
- earliest widening route is `Phase 6R / Packet 6R-C`
- the retained donor value there is:
- committed-face reconstruction flow
- face-vote replacement and correction ledger
- bounded final net shaping above committed faces
- ownership denied there is:
- do not let the repo absorb the full browser shell
- do not widen into broad solve explanation or generic solver UI transport
- do not copy or redistribute the bundled `frontend/lib/twistysim.min.js` asset without
preserving or replacing its upstream provenance/license
- do not let the row absorb the queued `MagicTile` topology lane
## Why this is now the next packet
The earlier `Phase 6R-B` queue head is already landed:
- retained classic-cube recognition calibration contract
- ordered face-observation contract
That means the live queue advances to:
- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver`
with the adjacent retained rows still ordered behind it:
- `Phase 6R-D` `roice3/MagicTile`
- the bounded speech-input / voice sidecar set
## Required result
The source-backed control pass for this packet is now complete.
The first actual `6R-C` implementation packet should:
- use the retained `0R-A` and `2R-A` authority surfaces plus the inspected
`rubix-cube-solver` source basis
- define one bounded retained slice from `rubix-cube-solver`
- keep the slice inside the accepted recognition-companion ownership
- widen only the first reconstruction family that can stand on its own without dragging in the full
browser shell, bundled frontend asset redistribution, or the next queued topology row
- 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/cv.py`
- `backend/server.py`
- `frontend/src/script.js`
Inspected first-party receiving basis:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockVisionClient.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp`
## Narrowed 6R-C slice decision
The first widening slice is now fixed as:
1. classic-cube committed-face reconstruction session plus face-vote replacement ledger
not as the broader browser or explanation shell.
That narrowed slice should cover:
- retained reconstruction-session contract types
- committed-face state and sticker-vote aggregation
- bounded correction-friendly replacement ledger for recommitted faces
- classic-cube missing-face accounting above committed observations
- final facelet snapshot and classic-net shaping when the six-face reconstruction becomes complete
- subsystem and contract-library routing for that bounded reconstruction session
- focused automation coverage for:
- reconstruction-session accumulation
- revision ledger behavior
- final classic-net shaping
## Deferred neighboring families
The first `6R-C` implementation packet must keep these capability families closed:
- browser/webcam shell
- broad solve explanation
- bundled `twistysim.min.js` redistribution
- generalized solver transport or remote-service ownership
- `MagicTile` or broader topology widening
- speech-input / voice sidecar ownership
Why this slice is first:
- the donor exposes its strongest narrow retained seam at committed-face reconstruction and
correction-friendly replacement, not at the full browser shell
- current first-party recognition code already had session, preview, and commit envelopes plus the
landed `qbr` ordered face-observation contract, but it still lacked an owned reconstruction
session that could accumulate corrected committed faces into a final classic-cube net
- broad explanation or frontend widening would prematurely absorb neighboring rows and bundled
asset obligations
## Out of scope for the first 6R-C packet
- full browser/webcam UI transplant
- broad solve explanation or recommendation shell
- bundled `frontend/lib/twistysim.min.js` redistribution
- generalized solver backend ownership
- `MagicTile` or broader topology widening
## Queue-adjacent legal sequencing guards
Do not omit the already-recorded legal caveats when the neighboring retained rows later come up:
- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver`
- preserve or replace the bundled `frontend/lib/twistysim.min.js` with its upstream
provenance/license intact before redistribution if any packet copies or ships that dependency
- `Phase 6R-D` `roice3/MagicTile`
- keep non-Euclidean topology ownership bounded to that later packet rather than letting the
current recognition companion lane absorb it by convenience
## Acceptance criteria
- the packet records why the live queue now advances from landed `6R-B` to `6R-C`
- the packet promotes `rubix-cube-solver` as the current queue head without widening the
neighboring retained rows prematurely
- the packet records that the first `6R-C` widening slice is committed-face reconstruction session
plus face-vote replacement ledger only
- the packet states exact out-of-scope families for the first `6R-C` pass
- the packet keeps the `twistysim.min.js` caveat visible in later queue sequencing
## Validation checklist
1. confirm the landed `Phase 6R-B` packet is now the consumed prior queue head
2. confirm the retained reconstruction seam in `rubix-cube-solver` narrows cleanly to committed
face aggregation and replacement ledger behavior
3. confirm the next packet is framed as bounded source-backed widening rather than README-only
routing
That is the packet.

View file

@ -3,7 +3,7 @@
Status update on `2026-05-20`:
- the canonical HyperTwist repo-row portfolio is now treated as `75` rows, not `71`
- currently implemented rows are now `26`, not `20`
- currently implemented rows are now `27`, not `20`
- four of the additional current implemented rows are the later-landed restrictive clean-room lanes:
- `cubing/alg.js`
- `cubing/twisty.js`
@ -13,6 +13,8 @@ Status update on `2026-05-20`:
- `HactarCE/Hyperspeedcube`
- the sixth additional current implemented row is the same-day bounded permissive partial row:
- `kkoomen/qbr`
- the seventh additional current implemented row is the same-day bounded permissive partial row:
- `vivaansinghvi07/rubix-cube-solver`
- the current next bounded move is **not** another restrictive packet by default
- the earlier first-party packet block that was still being treated as next is now confirmed landed:
- `d4f4ad3` `Add primary coach orchestration entry lane`
@ -29,8 +31,14 @@ Status update on `2026-05-20`:
- `kkoomen/qbr` remains partially incorporated; webcam UI shell, bundled-font redistribution,
multilingual solve shell, and multi-face reconstruction/correction/explanation families stay
deferred
- the current next bounded move is a source-backed `Phase 6R-C`
`vivaansinghvi07/rubix-cube-solver` preparation/control pass
- the generic source-backed `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` control pass is now
consumed
- the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face
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 current next bounded move is a source-backed `Phase 6R-D` `roice3/MagicTile`
preparation/control pass
- the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
waiting on another first-party packet
@ -58,8 +66,8 @@ Donor-strength rule:
- route difficulty changes how retained value may enter the product, not whether it may win technically
- current shallow-eval set: `75` repos
- currently verified live/implemented in checked Unreal surfaces: `26`
- permissive live lanes: `15`
- currently verified live/implemented in checked Unreal surfaces: `27`
- permissive live lanes: `16`
- boundary-sensitive live lanes: `6`
- restrictive live lanes: `5`
- the restrictive landed lanes are:
@ -69,7 +77,7 @@ Donor-strength rule:
- `HactarCE/2x2x2x2-Scrambler`
- `kash/cubedesk`
- each restrictive landed lane is to be treated as properly clean-roomed and implemented afterward
- `Phase 0R` is now closed for the remaining `49` non-live rows
- `Phase 0R` is now closed for the remaining `48` non-live rows
- `Phase 1R` is now closed as the retained-set contract and handoff overhaul
- `Phase 2R` is now closed as the retained-set ownership and acceptance packet sequence
- `Phase 3R-A` is now closed as the landed `Aarav2709/KubeTimr` timer subsystem widening packet
@ -89,17 +97,16 @@ Current routing truth:
- retained rows total: `68`
- discarded from the active retained set: `3`
- active non-live implementation-board rows: `37`
- active non-live implementation-board rows: `36`
- retained benchmark, oracle, or clean-room-later rows outside the active implementation board: `9`
The next bounded move is a source-backed `Phase 6R-C`
`vivaansinghvi07/rubix-cube-solver` preparation/control pass.
The next bounded move is a source-backed `Phase 6R-D` `roice3/MagicTile`
preparation/control pass.
Queue interpretation after that packet:
- then continue with the retained repo-row queue
- highest current repo-row queue pressure sits in:
- `vivaansinghvi07/rubix-cube-solver`
- `roice3/MagicTile`
- the bounded speech-input / voice sidecar set
- `HactarCE/Hyperspeedcube` remains a partially landed row rather than a closed row:
@ -118,13 +125,19 @@ Queue interpretation after that packet:
- webcam UI shell
- bundled-font redistribution and multilingual solve shell
- multi-face reconstruction / correction / explanation
- after the `Phase 6R-B` implementation packet, keep the adjacent legal sequencing guards visible:
- `kkoomen/qbr`
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate
confirmation or replacement in any future widening that would ship that asset
- `vivaansinghvi07/rubix-cube-solver` remains a partially landed row rather than a closed row:
- landed:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- 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:
- `vivaansinghvi07/rubix-cube-solver`
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing
its upstream provenance/license
its upstream provenance/license in any future widening that would ship that asset
- keep the optional `Phase 3R-G` browser comparison lane deferred unless the landed primary browser
spatial stack exposes a real gap