Implement Phase 6R-S correction resolution closure

This commit is contained in:
axiomlogicnexus 2026-05-24 18:21:32 +02:00
parent dafaf397cc
commit 4ea9e9683d
11 changed files with 1106 additions and 39 deletions

View file

@ -559,6 +559,22 @@ namespace HyperTwistContractLibraryInternal
return Profile;
}
bool HasAppliedResolutionForFace(
const TArray<FHyperTwistVisionCorrectionResolution>& ResolutionLedger,
const FHyperTwistVisionCommittedFaceState& FaceState
)
{
const FString NormalizedFaceId = NormalizeBrowserFaceHint(FaceState.FaceId);
return ResolutionLedger.ContainsByPredicate(
[&NormalizedFaceId, &FaceState](const FHyperTwistVisionCorrectionResolution& Resolution)
{
return NormalizeBrowserFaceHint(Resolution.FaceId) == NormalizedFaceId
&& Resolution.ObservationId == FaceState.LatestObservationId
&& Resolution.RevisionCount == FaceState.RevisionCount;
}
);
}
void ResolveMockClassicFaceSwatch(
const FString& FaceId,
FString& OutColorId,
@ -829,7 +845,8 @@ namespace HyperTwistContractLibraryInternal
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
const FHyperTwistVisionFaceObservation& ObservedFace,
const TArray<FString>& Warnings
const TArray<FString>& Warnings,
const TArray<FHyperTwistVisionCorrectionResolution>& ResolutionLedger
)
{
static constexpr float LowConfidenceThreshold = 0.85f;
@ -848,6 +865,12 @@ namespace HyperTwistContractLibraryInternal
CorrectionState.MissingFaceCount = ReconstructionSession.IsStructurallyValid()
? ReconstructionSession.MissingFaces.Num()
: 6;
CorrectionState.ResolutionLedger = ResolutionLedger;
CorrectionState.ResolvedCorrectionCount = ResolutionLedger.Num();
if (ResolutionLedger.Num() > 0)
{
CorrectionState.LastResolution = ResolutionLedger.Last();
}
CorrectionState.bManualCubeEditVisible =
CorrectionProfile.bSupportsManualCubeEdit && ReconstructionSession.bHasCompleteClassicCubeNet;
CorrectionState.bStageRestartVisible =
@ -861,6 +884,10 @@ namespace HyperTwistContractLibraryInternal
{
const FString FaceId = NormalizeBrowserFaceHint(FaceState.FaceId);
const bool bHasCompleteNet = ReconstructionSession.bHasCompleteClassicCubeNet;
if (HasAppliedResolutionForFace(ResolutionLedger, FaceState))
{
continue;
}
if (FaceState.bHasConflicts)
{
@ -1494,7 +1521,8 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe
SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace,
Result.Warnings
Result.Warnings,
TArray<FHyperTwistVisionCorrectionResolution>()
);
Result.SolveExplanationState = HyperTwistContractLibraryInternal::MakeClassicCubeSolveExplanationState(
SessionConfig,

View file

@ -2669,6 +2669,140 @@ namespace HyperTwistTrainingSubsystemInternal
return Profile;
}
bool IsCorrectionResolutionCommitKind(const FString& CommitKind)
{
return CommitKind.Equals(TEXT("correction-resolution"), ESearchCase::IgnoreCase)
|| CommitKind.Equals(TEXT("resolve-correction"), ESearchCase::IgnoreCase);
}
const FHyperTwistVisionCorrectionTarget* FindCorrectionTarget(
const FHyperTwistVisionCorrectionState& CorrectionState,
const FString& FaceId
)
{
const FString NormalizedFaceId = NormalizeRecognitionFaceId(FaceId);
return CorrectionState.PendingTargets.FindByPredicate(
[&NormalizedFaceId](const FHyperTwistVisionCorrectionTarget& Candidate)
{
return NormalizeRecognitionFaceId(Candidate.FaceId) == NormalizedFaceId;
}
);
}
FHyperTwistVisionFaceObservation BuildObservationFromCommittedFaceState(
const FHyperTwistVisionCommittedFaceState& FaceState
)
{
FHyperTwistVisionFaceObservation Observation;
Observation.ObservationId = FaceState.LatestObservationId;
Observation.FaceId = NormalizeRecognitionFaceId(FaceState.FaceId);
Observation.OrderingProfile = FaceState.OrderingProfile;
Observation.CandidateContourCount = FaceState.LatestOrderedStickers.Num();
Observation.AcceptedContourCount = FaceState.LatestOrderedStickers.Num();
Observation.bFoundStableThreeByThreeGrid = FaceState.LatestOrderedStickers.Num() == 9;
Observation.bOrderingStable = FaceState.LatestOrderedStickers.Num() == 9;
Observation.bCommitReady = true;
Observation.OrderedStickers = FaceState.LatestOrderedStickers;
return Observation;
}
bool HasAppliedResolutionForFace(
const TArray<FHyperTwistVisionCorrectionResolution>& ResolutionLedger,
const FHyperTwistVisionCommittedFaceState& FaceState
)
{
const FString NormalizedFaceId = NormalizeRecognitionFaceId(FaceState.FaceId);
return ResolutionLedger.ContainsByPredicate(
[&NormalizedFaceId, &FaceState](const FHyperTwistVisionCorrectionResolution& Resolution)
{
return NormalizeRecognitionFaceId(Resolution.FaceId) == NormalizedFaceId
&& Resolution.ObservationId == FaceState.LatestObservationId
&& Resolution.RevisionCount == FaceState.RevisionCount;
}
);
}
void UpsertCorrectionResolution(
TArray<FHyperTwistVisionCorrectionResolution>& ResolutionLedger,
const FHyperTwistVisionCorrectionResolution& Candidate,
bool& bAddedNewResolution
)
{
bAddedNewResolution = false;
const int32 ExistingIndex = ResolutionLedger.IndexOfByPredicate(
[&Candidate](const FHyperTwistVisionCorrectionResolution& Existing)
{
return NormalizeRecognitionFaceId(Existing.FaceId)
== NormalizeRecognitionFaceId(Candidate.FaceId)
&& Existing.ObservationId == Candidate.ObservationId
&& Existing.RevisionCount == Candidate.RevisionCount;
}
);
if (ExistingIndex == INDEX_NONE)
{
ResolutionLedger.Add(Candidate);
bAddedNewResolution = true;
return;
}
ResolutionLedger[ExistingIndex] = Candidate;
}
FHyperTwistVisionCorrectionResolution BuildCorrectionResolution(
const FHyperTwistVisionCommitRequest& Request,
const FHyperTwistVisionCorrectionState& CorrectionState,
const FHyperTwistVisionCommittedFaceState& FaceState
)
{
const FString FaceId = NormalizeRecognitionFaceId(FaceState.FaceId);
const FHyperTwistVisionCorrectionTarget* Target = FindCorrectionTarget(CorrectionState, FaceId);
FHyperTwistVisionCorrectionResolution Resolution;
Resolution.FaceId = FaceId;
Resolution.ObservationId = FaceState.LatestObservationId;
Resolution.RevisionCount = FaceState.RevisionCount;
Resolution.RequestedActionId = !Request.RequestedActionId.IsEmpty()
? Request.RequestedActionId
: (Target != nullptr && !Target->RecommendedActionId.IsEmpty()
? Target->RecommendedActionId
: TEXT("apply-manual-corrections"));
Resolution.ResolutionKind = !Request.ResolutionKind.IsEmpty()
? Request.ResolutionKind
: (FaceState.RevisionCount > 0
? TEXT("manual-review-accepted")
: TEXT("correction-review-accepted"));
Resolution.ResolutionId = FString::Printf(
TEXT("resolution-%s-%d-%s"),
*FaceId,
FaceState.RevisionCount,
*Resolution.ResolutionKind
);
for (const FHyperTwistVisionCorrectionContradiction& Contradiction : CorrectionState.Contradictions)
{
if (NormalizeRecognitionFaceId(Contradiction.FaceId) == FaceId)
{
Resolution.ResolvedContradictionIds.Add(Contradiction.ContradictionId);
}
}
Resolution.StatusLine = FString::Printf(
TEXT("Accepted face %s after manual correction review."),
*FaceId
);
Resolution.DetailLine = Resolution.ResolvedContradictionIds.Num() > 0
? FString::Printf(
TEXT("The current observation on face %s is now treated as reviewed in the bounded correction shell."),
*FaceId
)
: FString::Printf(
TEXT("The current bounded reading on face %s is accepted after manual review."),
*FaceId
);
return Resolution;
}
FHyperTwistVisionShellState BuildClassicCubeWebcamShellState(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
@ -2780,7 +2914,8 @@ namespace HyperTwistTrainingSubsystemInternal
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
const FHyperTwistVisionFaceObservation& ObservedFace,
const TArray<FString>& Warnings
const TArray<FString>& Warnings,
const TArray<FHyperTwistVisionCorrectionResolution>& ResolutionLedger
)
{
static constexpr float LowConfidenceThreshold = 0.85f;
@ -2799,6 +2934,12 @@ namespace HyperTwistTrainingSubsystemInternal
CorrectionState.MissingFaceCount = ReconstructionSession.IsStructurallyValid()
? ReconstructionSession.MissingFaces.Num()
: 6;
CorrectionState.ResolutionLedger = ResolutionLedger;
CorrectionState.ResolvedCorrectionCount = ResolutionLedger.Num();
if (ResolutionLedger.Num() > 0)
{
CorrectionState.LastResolution = ResolutionLedger.Last();
}
CorrectionState.bManualCubeEditVisible =
CorrectionProfile.bSupportsManualCubeEdit && ReconstructionSession.bHasCompleteClassicCubeNet;
CorrectionState.bStageRestartVisible =
@ -2812,6 +2953,10 @@ namespace HyperTwistTrainingSubsystemInternal
{
const FString FaceId = NormalizeRecognitionFaceId(FaceState.FaceId);
const bool bHasCompleteNet = ReconstructionSession.bHasCompleteClassicCubeNet;
if (HasAppliedResolutionForFace(ResolutionLedger, FaceState))
{
continue;
}
if (FaceState.bHasConflicts)
{
@ -4202,13 +4347,14 @@ FHyperTwistTrainingCoachPanelState UHyperTwistTrainingSubsystem::GetActiveCoachP
? TEXT("not-ready")
: *ActiveRecognitionSessionState.ServiceHealth.LastError);
PanelState.RecognitionStatusLine = FString::Printf(
TEXT("Recognition: %s | Service: %s | Session: %s | Frames %d | Commits %d | Corrections %d | Finalizations %d"),
TEXT("Recognition: %s | Service: %s | Session: %s | Frames %d | Commits %d | Corrections %d | Resolved %d | Finalizations %d"),
*RecognitionProviderLabel,
*RecognitionTransportState,
ActiveRecognitionSessionState.bSessionOpen ? TEXT("open") : TEXT("closed"),
ActiveRecognitionSessionState.SubmittedFrameCount,
ActiveRecognitionSessionState.CommittedObservationCount,
ActiveRecognitionSessionState.CorrectionEventCount,
ActiveRecognitionSessionState.ResolvedCorrectionCount,
ActiveRecognitionSessionState.FinalizedSessionCount
);
const bool bCoachPrefersRecognitionReview =
@ -7476,6 +7622,214 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit
NormalizedRequest.CommitKind = TEXT("face");
}
const bool bIsQbrClassicRoute =
HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig);
if (bIsQbrClassicRoute
&& HyperTwistTrainingSubsystemInternal::IsCorrectionResolutionCommitKind(NormalizedRequest.CommitKind))
{
Result.SessionId = NormalizedRequest.SessionId;
Result.CommitKind = NormalizedRequest.CommitKind;
Result.Snapshot.State.SourceSessionId = NormalizedRequest.SessionId;
Result.ReconstructionSession = ActiveRecognitionSessionState.ActiveReconstructionSession;
Result.MissingUnits = Result.ReconstructionSession.MissingFaces;
Result.Confidence = Result.ReconstructionSession.ConfidenceRollup;
const FHyperTwistVisionFaceObservation ReferenceObservation =
ActiveRecognitionSessionState.LastCommitResult.ObservedFace.IsStructurallyValid()
? ActiveRecognitionSessionState.LastCommitResult.ObservedFace
: ActiveRecognitionSessionState.LastPreviewResult.ObservedFace;
Result.CorrectionState =
HyperTwistTrainingSubsystemInternal::BuildClassicCubeCorrectionState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
ReferenceObservation,
Result.Warnings,
ActiveRecognitionSessionState.CorrectionResolutionLedger
);
const FString RequestedFaceId = !NormalizedRequest.TargetFace.IsEmpty()
? HyperTwistTrainingSubsystemInternal::NormalizeRecognitionFaceId(NormalizedRequest.TargetFace)
: Result.CorrectionState.ActiveTargetFaceId;
const FHyperTwistVisionCorrectionTarget* RequestedTarget =
HyperTwistTrainingSubsystemInternal::FindCorrectionTarget(Result.CorrectionState, RequestedFaceId);
const FHyperTwistVisionCommittedFaceState* RequestedFaceState =
HyperTwistTrainingSubsystemInternal::FindCommittedFaceState(Result.ReconstructionSession, RequestedFaceId);
bool bAppliedResolution = false;
bool bAddedNewResolution = false;
if (!Result.ReconstructionSession.IsStructurallyValid())
{
Result.Warnings.Add(TEXT("correction-resolution-unavailable"));
}
else if (!Result.ReconstructionSession.bHasCompleteClassicCubeNet)
{
Result.Warnings.Add(TEXT("correction-resolution-requires-complete-net"));
}
else if (RequestedFaceId.IsEmpty())
{
Result.Warnings.Add(TEXT("correction-target-face-required"));
}
else if (RequestedTarget == nullptr)
{
Result.Warnings.Add(TEXT("correction-target-unavailable"));
}
else if (RequestedTarget->RecommendedActionId != TEXT("apply-manual-corrections"))
{
Result.Warnings.Add(TEXT("correction-target-does-not-support-manual-resolution"));
}
else if (RequestedFaceState == nullptr)
{
Result.Warnings.Add(TEXT("correction-face-state-unavailable"));
}
else
{
Result.CommittedUnit = RequestedFaceId;
Result.ObservedFace =
HyperTwistTrainingSubsystemInternal::BuildObservationFromCommittedFaceState(*RequestedFaceState);
FHyperTwistVisionCorrectionResolution AppliedResolution =
HyperTwistTrainingSubsystemInternal::BuildCorrectionResolution(
NormalizedRequest,
Result.CorrectionState,
*RequestedFaceState
);
HyperTwistTrainingSubsystemInternal::UpsertCorrectionResolution(
ActiveRecognitionSessionState.CorrectionResolutionLedger,
AppliedResolution,
bAddedNewResolution
);
ActiveRecognitionSessionState.LastCorrectionResolution = AppliedResolution;
if (bAddedNewResolution)
{
++ActiveRecognitionSessionState.ResolvedCorrectionCount;
}
FHyperTwistPuzzleState ReconstructedState;
FHyperTwistStateSnapshot ReconstructedSnapshot;
if (HyperTwistTrainingSubsystemInternal::TryBuildClassicRecognitionStateSnapshot(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
ReconstructedState,
ReconstructedSnapshot
))
{
Result.Snapshot = ReconstructedSnapshot;
}
else
{
Result.Snapshot = FHyperTwistStateSnapshot();
Result.Warnings.AddUnique(TEXT("recognition-reconstruction-unavailable"));
}
Result.ShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeWebcamShellState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
RequestedFaceId,
Result.CommittedUnit
);
Result.BrowserShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeBrowserShellState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace
);
Result.CorrectionState =
HyperTwistTrainingSubsystemInternal::BuildClassicCubeCorrectionState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace,
Result.Warnings,
ActiveRecognitionSessionState.CorrectionResolutionLedger
);
AppliedResolution.bClearsSolveExplanationBlock = !Result.CorrectionState.bSolveExplanationBlocked;
ActiveRecognitionSessionState.LastCorrectionResolution = AppliedResolution;
HyperTwistTrainingSubsystemInternal::UpsertCorrectionResolution(
ActiveRecognitionSessionState.CorrectionResolutionLedger,
AppliedResolution,
bAddedNewResolution
);
Result.AppliedCorrectionResolution = AppliedResolution;
Result.CorrectionState.LastResolution = AppliedResolution;
Result.SolveExplanationState =
HyperTwistTrainingSubsystemInternal::BuildClassicCubeSolveExplanationState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
Result.CorrectionState
);
bAppliedResolution = true;
}
if (!Result.ObservedFace.IsStructurallyValid() && RequestedFaceState != nullptr)
{
Result.ObservedFace =
HyperTwistTrainingSubsystemInternal::BuildObservationFromCommittedFaceState(*RequestedFaceState);
}
if (!Result.ShellState.IsStructurallyValid())
{
Result.ShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeWebcamShellState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
RequestedFaceId,
Result.CommittedUnit
);
}
if (!Result.BrowserShellState.IsStructurallyValid())
{
Result.BrowserShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeBrowserShellState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace
);
}
if (!Result.CorrectionState.IsStructurallyValid())
{
Result.CorrectionState =
HyperTwistTrainingSubsystemInternal::BuildClassicCubeCorrectionState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace,
Result.Warnings,
ActiveRecognitionSessionState.CorrectionResolutionLedger
);
}
if (!Result.SolveExplanationState.IsStructurallyValid())
{
Result.SolveExplanationState =
HyperTwistTrainingSubsystemInternal::BuildClassicCubeSolveExplanationState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
Result.CorrectionState
);
}
if (bAppliedResolution)
{
FHyperTwistReplayRecognitionPayload CorrectionPayload;
CorrectionPayload.Snapshot = Result.Snapshot;
CorrectionPayload.Confidence = Result.Confidence;
CorrectionPayload.CommittedFaceOrStage = !RequestedFaceId.IsEmpty()
? RequestedFaceId
: NormalizedRequest.CommitKind;
AppendRecognitionReplayEvent(
EHyperTwistReplayEventType::RecognitionCorrection,
CorrectionPayload,
INDEX_NONE
);
++ActiveRecognitionSessionState.CorrectionEventCount;
}
ActiveRecognitionSessionState.bHasCommitResult = true;
ActiveRecognitionSessionState.LastCommitResult = Result;
ActiveRecognitionSessionState.LastUpdatedAtUtc = FDateTime::UtcNow().ToIso8601();
ActiveRecognitionSessionState.LastError =
Result.Warnings.Num() > 0 ? FString::Join(Result.Warnings, TEXT(" | ")) : FString();
ActiveRecognitionSessionState.ServiceHealth.LastError = ActiveRecognitionSessionState.LastError;
if (bAppliedResolution)
{
SynchronizeRecognitionReplayMutation();
}
return Result;
}
Result = VisionClient->CommitVisionObservation(NormalizedRequest);
if (Result.SessionId.IsEmpty())
{
@ -7560,7 +7914,8 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace,
Result.Warnings
Result.Warnings,
ActiveRecognitionSessionState.CorrectionResolutionLedger
);
Result.SolveExplanationState =
HyperTwistTrainingSubsystemInternal::BuildClassicCubeSolveExplanationState(

View file

@ -886,6 +886,51 @@ struct FHyperTwistVisionCorrectionContradiction
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionCorrectionResolution
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ResolutionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FaceId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ResolutionKind;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RequestedActionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ObservationId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 RevisionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> ResolvedContradictionIds;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bClearsSolveExplanationBlock = false;
bool IsStructurallyValid() const
{
return !ResolutionId.IsEmpty()
&& !FaceId.IsEmpty()
&& !ResolutionKind.IsEmpty()
&& !RequestedActionId.IsEmpty()
&& RevisionCount >= 0;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionCorrectionProfile
{
@ -963,6 +1008,9 @@ struct FHyperTwistVisionCorrectionState
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ContradictionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ResolvedCorrectionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionCorrectionTarget ActiveTarget;
@ -972,6 +1020,12 @@ struct FHyperTwistVisionCorrectionState
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionCorrectionContradiction> Contradictions;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionCorrectionResolution LastResolution;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionCorrectionResolution> ResolutionLedger;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCorrectionRequired = false;
@ -998,7 +1052,9 @@ struct FHyperTwistVisionCorrectionState
if (CorrectionProfileId.IsEmpty()
|| MissingFaceCount < 0
|| ContradictionCount < 0
|| ContradictionCount != Contradictions.Num())
|| ContradictionCount != Contradictions.Num()
|| ResolvedCorrectionCount < 0
|| ResolvedCorrectionCount != ResolutionLedger.Num())
{
return false;
}
@ -1029,6 +1085,19 @@ struct FHyperTwistVisionCorrectionState
}
}
if (!LastResolution.ResolutionId.IsEmpty() && !LastResolution.IsStructurallyValid())
{
return false;
}
for (const FHyperTwistVisionCorrectionResolution& Resolution : ResolutionLedger)
{
if (!Resolution.IsStructurallyValid())
{
return false;
}
}
return true;
}
};
@ -1175,6 +1244,12 @@ struct FHyperTwistVisionCommitRequest
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TargetStage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RequestedActionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ResolutionKind;
};
USTRUCT(BlueprintType)
@ -1215,6 +1290,9 @@ struct FHyperTwistVisionCommitResult
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionCorrectionState CorrectionState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionCorrectionResolution AppliedCorrectionResolution;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> Warnings;

View file

@ -1868,6 +1868,9 @@ struct FHyperTwistTrainingRecognitionSessionState
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CorrectionEventCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ResolvedCorrectionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 FinalizedSessionCount = 0;
@ -1886,6 +1889,12 @@ struct FHyperTwistTrainingRecognitionSessionState
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionFinalizeResult LastFinalizeResult;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionCorrectionResolution LastCorrectionResolution;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionCorrectionResolution> CorrectionResolutionLedger;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionReconstructionSession ActiveReconstructionSession;
};

View file

@ -0,0 +1,235 @@
// 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 HyperTwistClassicCubePhase6RSTestInternal
{
FHyperTwistTrainingDeck MakeRecognitionDeck()
{
FHyperTwistTrainingDeck Deck;
Deck.DeckId = TEXT("phase6r-s/classic-cube-correction-resolution");
Deck.Title = TEXT("Phase 6R-S Classic Cube Correction Resolution");
Deck.DeliveryModes = {
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
EHyperTwistTrainingDeliveryMode::CoachReviewed
};
FHyperTwistTrainingCase TrainingCase;
TrainingCase.CaseId = TEXT("phase6r-s-case");
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition;
TrainingCase.PromptLabel = TEXT("Phase 6R-S 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-s-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);
}
FHyperTwistVisionCommitResult ResolveCorrection(
UHyperTwistTrainingSubsystem* TrainingSubsystem,
const FString& FaceId
)
{
FHyperTwistVisionCommitRequest Request;
Request.CommitKind = TEXT("correction-resolution");
Request.TargetFace = FaceId;
Request.TargetStage = TEXT("cube-edit");
Request.RequestedActionId = TEXT("apply-manual-corrections");
Request.ResolutionKind = TEXT("manual-review-accepted");
return TrainingSubsystem->CommitActiveRecognitionObservation(Request);
}
void CommitClassicCubeNet(UHyperTwistTrainingSubsystem* TrainingSubsystem)
{
const TArray<FString> FaceOrder = {
TEXT("U"),
TEXT("R"),
TEXT("F"),
TEXT("D"),
TEXT("L"),
TEXT("B")
};
for (int32 FaceOrdinal = 0; FaceOrdinal < FaceOrder.Num(); ++FaceOrdinal)
{
CommitFace(TrainingSubsystem, FaceOrder[FaceOrdinal], FaceOrdinal + 1);
}
}
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistClassicCubePhase6RSCorrectionResolutionUnlockTest,
"HyperTwist.Permissive.ClassicCube.Phase6R.S.CorrectionResolutionUnlock",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistClassicCubePhase6RSCorrectionResolutionUnlockTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistClassicCubePhase6RSTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-s-unlock-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-S."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
HyperTwistClassicCubePhase6RSTestInternal::CommitClassicCubeNet(TrainingSubsystem);
HyperTwistClassicCubePhase6RSTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 7);
const FHyperTwistVisionCommitResult Result =
HyperTwistClassicCubePhase6RSTestInternal::ResolveCorrection(TrainingSubsystem, TEXT("F"));
const FHyperTwistTrainingRecognitionSessionState SessionState =
TrainingSubsystem->GetActiveRecognitionSessionState();
TestTrue(TEXT("The resolution route must surface a structurally valid applied resolution."), Result.AppliedCorrectionResolution.IsStructurallyValid());
TestEqual(TEXT("The resolution route must apply the reviewed face explicitly."), Result.AppliedCorrectionResolution.FaceId, TEXT("F"));
TestTrue(TEXT("The resolution route must carry a structurally valid correction state."), Result.CorrectionState.IsStructurallyValid());
TestFalse(TEXT("The resolution route must close the open correction target after review."), Result.CorrectionState.bCorrectionRequired);
TestTrue(TEXT("The resolution route must mark the correction shell complete after review."), Result.CorrectionState.bCorrectionComplete);
TestEqual(TEXT("The resolution route must retain one correction resolution in the ledger."), Result.CorrectionState.ResolutionLedger.Num(), 1);
TestEqual(TEXT("The resolution route must count one resolved correction event."), Result.CorrectionState.ResolvedCorrectionCount, 1);
TestTrue(TEXT("Solve guidance must unlock once the correction review closes."), Result.SolveExplanationState.bReadyForExplanation);
TestEqual(TEXT("The solve explanation route must return to the bounded first solve stage."), Result.SolveExplanationState.RecommendedStageId, TEXT("orient-centers"));
TestEqual(TEXT("The session must count one resolved correction event."), SessionState.ResolvedCorrectionCount, 1);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistClassicCubePhase6RSCorrectionResolutionPartialClosureTest,
"HyperTwist.Permissive.ClassicCube.Phase6R.S.CorrectionResolutionPartialClosure",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistClassicCubePhase6RSCorrectionResolutionPartialClosureTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistClassicCubePhase6RSTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-s-partial-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-S."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
HyperTwistClassicCubePhase6RSTestInternal::CommitClassicCubeNet(TrainingSubsystem);
HyperTwistClassicCubePhase6RSTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 7);
HyperTwistClassicCubePhase6RSTestInternal::CommitFace(TrainingSubsystem, TEXT("R"), 8);
const FHyperTwistVisionCommitResult Result =
HyperTwistClassicCubePhase6RSTestInternal::ResolveCorrection(TrainingSubsystem, TEXT("F"));
const FHyperTwistTrainingRecognitionSessionState SessionState =
TrainingSubsystem->GetActiveRecognitionSessionState();
TestTrue(TEXT("The partial closure route must still surface an applied resolution."), Result.AppliedCorrectionResolution.IsStructurallyValid());
TestTrue(TEXT("The partial closure route must keep correction open while another revised face remains."), Result.CorrectionState.bCorrectionRequired);
TestFalse(TEXT("The partial closure route must not mark the shell complete while another face is pending."), Result.CorrectionState.bCorrectionComplete);
TestEqual(TEXT("The partial closure route must leave the earlier-ordered revised face active."), Result.CorrectionState.ActiveTargetFaceId, TEXT("R"));
TestEqual(TEXT("The partial closure route must classify the remaining active target as a revised face."), Result.CorrectionState.ActiveTarget.ReasonId, TEXT("revised-face"));
TestEqual(TEXT("The partial closure route must preserve one ledger entry after the first accepted review."), Result.CorrectionState.ResolutionLedger.Num(), 1);
TestFalse(TEXT("Solve guidance must stay blocked while another correction target remains."), Result.SolveExplanationState.bReadyForExplanation);
TestEqual(TEXT("The solve explanation route must stay in cube-edit while another review remains."), Result.SolveExplanationState.RecommendedStageId, TEXT("cube-edit"));
TestEqual(TEXT("The session must still count only one resolved correction review."), SessionState.ResolvedCorrectionCount, 1);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistClassicCubePhase6RSCorrectionResolutionReopenTest,
"HyperTwist.Permissive.ClassicCube.Phase6R.S.CorrectionResolutionReopen",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistClassicCubePhase6RSCorrectionResolutionReopenTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistClassicCubePhase6RSTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-s-reopen-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-S."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
HyperTwistClassicCubePhase6RSTestInternal::CommitClassicCubeNet(TrainingSubsystem);
HyperTwistClassicCubePhase6RSTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 7);
HyperTwistClassicCubePhase6RSTestInternal::ResolveCorrection(TrainingSubsystem, TEXT("F"));
const FHyperTwistVisionCommitResult Result =
HyperTwistClassicCubePhase6RSTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 8);
const FHyperTwistTrainingRecognitionSessionState SessionState =
TrainingSubsystem->GetActiveRecognitionSessionState();
TestTrue(TEXT("The reopen route must keep a structurally valid correction state."), Result.CorrectionState.IsStructurallyValid());
TestTrue(TEXT("The reopen route must reopen correction after a later recommit on the same face."), Result.CorrectionState.bCorrectionRequired);
TestEqual(TEXT("The reopen route must target the recommitted face again."), Result.CorrectionState.ActiveTargetFaceId, TEXT("F"));
TestEqual(TEXT("The reopen route must classify the reopened target as a revised face."), Result.CorrectionState.ActiveTarget.ReasonId, TEXT("revised-face"));
TestEqual(TEXT("The reopen route must preserve the earlier accepted review in the ledger."), Result.CorrectionState.ResolutionLedger.Num(), 1);
TestFalse(TEXT("Solve guidance must block again after a later recommit reopens correction."), Result.SolveExplanationState.bReadyForExplanation);
TestEqual(TEXT("The session must not double-count a new resolution when only a recommit occurred."), SessionState.ResolvedCorrectionCount, 1);
return true;
}
#endif

View file

@ -159,8 +159,12 @@ Status update on `2026-05-21`:
correction/explanation control pass is now consumed
- the bounded permissive `Phase 6R-R` shared classic-cube recognition multi-face
correction/explanation shell packet is now landed in current code
- the current next bounded move is a source-backed follow-on control pass for the retained
classic-cube recognition correction/explanation shell remainder, not a new restrictive packet by
- the generic source-backed `Phase 6R-S` shared classic-cube correction-resolution closure
control pass is now consumed
- the bounded permissive `Phase 6R-S` shared classic-cube correction-resolution closure packet is
now landed in current code
- the current next bounded move is a source-backed `Phase 6R-T` `roice3/MagicTile`
transform-aware macro remapping 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
@ -265,8 +269,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 follow-on control pass for the retained classic-cube
recognition correction/explanation shell remainder
- the next bounded move is a source-backed `Phase 6R-T` `roice3/MagicTile`
transform-aware macro remapping preparation/control pass
Companion docs:
@ -755,7 +759,8 @@ Approved working posture:
- classic-cube recognition calibration contract
- ordered face-observation contract
- classic-cube webcam shell
- shared correction-target capture-state and re-scan adjunct via `Phase 6R-R`
- shared correction-target capture-state and re-scan adjunct via the landed shared `Phase 6R-R`
and `Phase 6R-S` correction shell
- keep the following families deferred to later queue decisions:
- bundled-font redistribution and multilingual solve shell
- direct standalone multilingual or asset-shipping widening beyond the landed shared correction
@ -805,7 +810,7 @@ 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 two bounded retained slices are now landed in current code:
- the bounded retained slices now landed in current code are:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
@ -813,11 +818,13 @@ Approved working posture:
- bounded solve explanation/recommendation stage ladder and recommendation state
- bounded correction profile, contradiction-aware correction state, and
correction-explanation shell routing
- correction-resolution ledger, manual-review acceptance, and solve-guidance reopen/unlock
semantics
- keep the row only partially incorporated by default:
- retained correction/explanation shell remainder beyond the landed
correction-state slice still requires a separate control decision
- any redistribution of `frontend/lib/twistysim.min.js` still requires preserved or replaced
upstream provenance/license
- generalized solver backend ownership and broad playback-runtime ownership stay separately
deferred
### `tentone/rubix-solver`
@ -3147,8 +3154,8 @@ Approved working posture:
- keep ordinary third-party notices if code or substantial portions are incorporated
- keep the live widening bounded to the landed tiling-topology contract family rather than
flattening the row into blanket permission for full host-shell or runtime replacement use
- after the landed `Phase 6R-D` slice, move the queue head to the bounded speech-input / voice
sidecar set rather than reopening `MagicTile` by default
- the next justified widening is now the bounded `Phase 6R-T` transform-aware macro remapping
control pass rather than blanket host-shell or runtime replacement use
### `superliminal.com/andrey/mc7d`

View file

@ -0,0 +1,173 @@
# HyperTwist Phase 6R-S classic-cube correction resolution closure implementation packet
Created on `2026-05-24`
## Status
- first-party HyperTwist packet
- bounded permissive `Phase 6R-S` implementation slice
## Purpose
This packet lands the current retained shared classic-cube
correction-resolution closure family:
- first-party accepted-resolution ledger state, manual-review acceptance
routing, and solve-guidance reopen/unlock semantics above the already landed
`qbr` webcam shell, `rubix-cube-solver` reconstruction/browser-shell/solve
explanation seams, and the first-party `Phase 6R-R` correction shell
It is not:
- a full `qbr` row transplant
- a full `rubix-cube-solver` row transplant
- a bundled `twistysim.min.js` redistribution packet
- a bundled `arial-unicode-ms.ttf` redistribution packet
- a multilingual `qbr` shell packet
- a generalized solver backend packet
- a broad playback-runtime 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_S_CLASSIC_CUBE_CORRECTION_RESOLUTION_CLOSURE_PREPARATION_PACKET_2026-05-24.md`
The retained owner split stays explicit:
- `vivaansinghvi07/rubix-cube-solver`
- retained primary owner for correction/explanation semantics,
correction-resolution closure, and solve-guidance reopen/unlock behavior
- `kkoomen/qbr`
- retained adjunct owner for capture-state continuity, target-face re-scan
anchoring, and revision-aware reopen posture
The top-level product shell owner does not change here:
- first-party HyperTwist remains the broader recognition-session and
product-shell owner
## Landed scope
The current code now owns a bounded first-party correction-resolution closure
slice through:
- first-party `HyperTwistRecognition` contract types for:
- correction-resolution record shape
- correction-state resolution ledger and last-resolution readout
- commit-request manual-review action metadata
- commit-result applied-resolution payloads
- first-party recognition session-state normalization for:
- resolved correction count
- last correction resolution
- correction-resolution ledger persistence across the active session
- active subsystem normalization in:
- local correction-resolution commit handling above the landed qbr classic
route
- correction-target validation before review acceptance
- resolution upsert behavior keyed by face id plus observation/revision
identity
- solve-guidance unlock after full closure
- correction reopen after a later recommit changes the reviewed face state
- sample contract routing in:
- `UHyperTwistContractLibrary`
- focused automation coverage in:
- `HyperTwistClassicCubePhase6RSCorrectionResolutionContractTest.cpp`
## Why this is still intentionally bounded
This packet lands the next bounded correction-resolution closure slice, but it
does not widen into neighboring families or broader ownership.
Still excluded:
- bundled `twistysim.min.js` redistribution
- bundled `arial-unicode-ms.ttf` redistribution
- standalone multilingual `qbr` shell ownership
- generalized solver backend ownership
- broad playback-runtime ownership
- donor-shaped frontend transplant
## Validation
Build validation:
- `C:\Program Files\Epic Games\UE_5.7\Engine\Build\BatchFiles\Build.bat UnrealHyperTwistEditor Win64 Development -Project='C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject' -WaitMutex -NoHotReloadFromIDE -NoUba`
Focused automation validation:
- `C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject -unattended -nop4 -nosplash -NullRHI -log -stdout -FullStdOutLogOutput -AbsLog=C:\HyperTwist\UnrealHyperTwist\Saved\Logs\Phase6R-S-ClassicCube-Verify.log -ReportExportPath=C:\HyperTwist\UnrealHyperTwist\Saved\AutomationReports\Phase6R-S-ClassicCube-Verify -ExecCmds="Automation RunTests HyperTwist.Permissive.ClassicCube.Phase6R.S; Quit" -TestExit="Automation Test Queue Empty"`
Regression automation validation:
- `HyperTwist.Permissive.ClassicCube.Phase6R.R`
- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q`
- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.P`
- `HyperTwist.Permissive.Qbr.Phase6R.O`
- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.C`
- `HyperTwist.CleanRoom.CubeDesk`
Expected covered tests:
- `CorrectionResolutionUnlock`
- `CorrectionResolutionPartialClosure`
- `CorrectionResolutionReopen`
- existing `Phase 6R-R`, `rubix-cube-solver`, `qbr`, and `CubeDesk`
regression suites
## Queue effect
This packet consumes the current bounded `Phase 6R-S` implementation slice.
`kkoomen/qbr` remains partially incorporated:
- landed now:
- classic-cube recognition calibration contract
- ordered face-observation contract
- classic-cube webcam shell
- shared correction-target capture-state and re-scan adjunct
- still deferred:
- bundled-font redistribution and multilingual solve shell
`vivaansinghvi07/rubix-cube-solver` remains partially incorporated:
- landed now:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- browser/webcam shell profile and browser-shell session-state composition
- bounded solve explanation/recommendation stage ladder and recommendation
state
- bounded correction profile, contradiction-aware correction state, and
correction-explanation shell routing
- correction-resolution ledger, manual-review acceptance, and
solve-guidance reopen/unlock semantics
- still deferred:
- bundled `twistysim.min.js` redistribution
- generalized solver backend ownership
- broad playback-runtime ownership
The next clean move is:
- a source-backed `Phase 6R-T` `roice3/MagicTile` transform-aware macro
remapping preparation/control pass
Keep the future sequencing guards visible:
- `vivaansinghvi07/rubix-cube-solver`
- do not copy or redistribute `frontend/lib/twistysim.min.js` without
preserving or replacing its upstream provenance/license in any future
widening that would ship that asset
- `kkoomen/qbr`
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without
separate confirmation or replacement in any future widening that would
ship that asset
- `roice3/MagicTile`
- keep the next widening bounded to transform-aware macro remapping rather
than broad non-Euclidean interaction or host-shell ownership
- first-party HyperTwist
- keep the shared classic-cube correction shell closed by default unless a
narrower first-party gap is proven above the landed resolution-closure
seam

View file

@ -0,0 +1,168 @@
# HyperTwist Phase 6R-S classic-cube correction resolution closure preparation packet
Created on `2026-05-24`
## Status
- historical consumed preparation authority
- bounded post-`Phase 6R-R` control slice
- the first bounded implementation slice now lands in:
- `docs/arch/HYPERTWIST_PHASE6R_S_CLASSIC_CUBE_CORRECTION_RESOLUTION_CLOSURE_IMPLEMENTATION_PACKET_2026-05-24.md`
## Purpose
This packet freezes the next widening order after the landed `Phase 6R-R`
shared classic-cube correction/explanation shell slice.
The open task was:
- define the next bounded source-backed widening packet as the shared
classic-cube correction-resolution closure layer above the already landed
`qbr` webcam shell, `rubix-cube-solver` reconstruction/browser shell/solve
explanation seams, and the first-party `Phase 6R-R` correction shell
It is not:
- a full `qbr` row transplant
- a full `rubix-cube-solver` row transplant
- a bundled `twistysim.min.js` redistribution packet
- a bundled `arial-unicode-ms.ttf` redistribution packet
- a multilingual `qbr` shell packet
- a generalized solver backend packet
- a broad playback-runtime 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_R_CLASSIC_CUBE_MULTI_FACE_CORRECTION_EXPLANATION_IMPLEMENTATION_PACKET_2026-05-24.md`
The key accepted routing facts are:
- `kkoomen/qbr` remains the primary classic-cube live-recognition and
capture-state donor
- `vivaansinghvi07/rubix-cube-solver` remains the stronger correction,
reconstruction-companion, and explanation-semantics donor
- the already landed `Phase 6R-R` slice owns:
- correction targets
- contradiction objects
- correction-profile defaults
- correction shell visibility and solve-guidance blocking
- the next donor value strongest for the narrower shared slice is:
- accepted manual-review resolution per face/revision
- explicit correction-resolution ledger state
- reopen-after-recommit behavior
- clear-to-solve transition semantics after correction review closure
Ownership denied here is:
- do not widen into bundled `twistysim.min.js` redistribution
- do not widen into bundled `arial-unicode-ms.ttf` redistribution
- do not widen into standalone multilingual `qbr` ownership
- do not widen into generalized solver backend ownership
- do not widen into broad playback-runtime ownership
## Cross-lane retained split
Exact contested slice:
- classic-cube correction-resolution closure
Candidate repos considered:
- `kkoomen/qbr`
- `vivaansinghvi07/rubix-cube-solver`
Authority outcome:
- `vivaansinghvi07/rubix-cube-solver` - `A1 / R1 / F2`
- retained primary owner for correction-resolution semantics, manual-review
acceptance flow, and solve-guidance reopen/unlock behavior
- `kkoomen/qbr` - `A2 / R1 / F2`
- retained adjunct owner for capture-state continuity, target-face re-scan
anchoring, and revision-aware reopen posture that keeps the correction
shell attached to the recognition substrate
The top-level product shell owner does not change here:
- first-party HyperTwist remains the broader recognition-session and
product-shell owner
## Required result
The source-backed control pass for this packet is now complete.
The first actual `6R-S` implementation packet should:
- define one bounded correction-resolution closure slice
- keep the slice above the landed `Phase 6R-O`, `Phase 6R-P`, `Phase 6R-Q`,
and `Phase 6R-R` seams
- land accepted-resolution ledger state, manual-review acceptance routing, and
solve-guidance reopen/unlock semantics
- preserve `rubix-cube-solver` as the retained correction/explanation-semantic
owner while preserving `qbr` as the capture-state adjunct
- 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:
- `C:\visual_studio_solutions\multi_project\GPT 5.4 HyperTwist parse\19-kkoomen-qbr-upstream-dossier.md`
- `C:\visual_studio_solutions\multi_project\GPT 5.4 HyperTwist parse\20-vivaansinghvi07-rubix-cube-solver-upstream-dossier.md`
Inspected first-party receiving basis:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistClassicCubePhase6RRCorrectionContractTest.cpp`
## Narrowed 6R-S slice decision
The next widening slice is fixed as:
1. correction-resolution closure and solve-guidance reopen/unlock semantics
That narrowed slice covers:
- first-party accepted-resolution ledger state for:
- reviewed face id
- observation/revision identity
- resolved contradiction ids
- clear-to-solve transition visibility
- first-party resolution commit behavior for:
- manual-review acceptance
- correction-target validation
- replay mutation and correction-event accounting
- first-party reopen behavior for:
- later recommits on a previously resolved face
- solve-guidance blocking when a later revision reopens correction
- focused automation coverage
## Deferred neighboring families
The first `6R-S` implementation packet must keep these capability families
closed:
- bundled `twistysim.min.js` shipping or redistribution
- bundled `arial-unicode-ms.ttf` shipping or redistribution
- standalone multilingual `qbr` shell ownership
- generalized solver backend ownership
- broad playback-runtime ownership
- any donor-shaped frontend transplant
## Queue effect
This preparation packet is now consumed by the landed bounded `Phase 6R-S`
implementation slice.
Once that slice lands, the next clean move should return to the retained
repo-row queue at:
- a source-backed `Phase 6R-T` `roice3/MagicTile` transform-aware macro
remapping preparation/control pass

View file

@ -108,15 +108,21 @@ The next bounded move is now:
correction/explanation control pass is now consumed
10. the bounded permissive `Phase 6R-R` shared classic-cube recognition multi-face
correction/explanation shell packet is now landed in current code
11. the next bounded move is a source-backed follow-on control pass for the retained
classic-cube recognition correction/explanation shell remainder shared across `kkoomen/qbr`
and `vivaansinghvi07/rubix-cube-solver`
12. keep the `rubix-cube-solver` guard visible:
11. the generic source-backed `Phase 6R-S` shared classic-cube correction-resolution closure
control pass is now consumed
12. the bounded permissive `Phase 6R-S` shared classic-cube correction-resolution closure packet
is now landed in current code
13. the next bounded move is a source-backed `Phase 6R-T` `roice3/MagicTile`
transform-aware macro remapping preparation/control pass
14. keep the `rubix-cube-solver` guard visible:
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing
its upstream provenance/license in any future widening that would ship that asset
13. keep the `qbr` guard visible:
15. 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
16. keep the `MagicTile` guard visible:
- keep the next widening bounded to transform-aware macro remapping rather than broad
non-Euclidean interaction or WinForms/OpenTK host-shell ownership
## Memory-specific sequencing rule

View file

@ -112,6 +112,7 @@ Do not collapse those three tiers into one undifferentiated "features" voice.
| Committed-face reconstruction and final classic-net shaping | Implemented now | landed `rubix-cube-solver` packet | Reconstruction session boundary is live. |
| Browser-assisted recognition shell | Implemented now | landed `rubix-cube-solver` `Phase 6R-P` | First-party browser-shell profile, websocket session metadata, stage routing, and manual-edit handoff are live. |
| Bounded solve explanation and recommendation shell | Implemented now | landed `rubix-cube-solver` `Phase 6R-Q` | First-party stage-ladder guidance and recommendation-state readout are live after full reconstruction. |
| Correction-resolution closure shell | Implemented now | landed shared `Phase 6R-S` packet | First-party accepted-resolution ledger, per-face manual-review acceptance, and solve-guidance reopen/unlock semantics are live above the bounded correction shell. |
| 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. |
@ -151,7 +152,7 @@ repo.
| Bounded solve explanation/recommendation shell | Implemented now | `rubix-cube-solver` bounded packet | Live stage-ladder recommendation state above the bounded browser recognition shell. |
| Provider-backed recognition service contract | Implemented now | first-party recognition client surfaces | First-party normalized recognition session boundary is real. |
| Multilingual recognition guidance shell | Deep-source grounded retained | `qbr` retained remainder | Kept deferred until a font-safe and provenance-safe shipping path is explicit. |
| Multi-face correction/explanation shell | Implemented now | first-party recognition/training surfaces | Current bounded correction profile, contradiction-aware correction state, and correction-explanation shell are live above the landed `Phase 6R-O`, `Phase 6R-P`, and `Phase 6R-Q` seams. Broader playback/runtime and bundled-asset widening remain deferred. |
| Multi-face correction/explanation shell | Implemented now | first-party recognition/training surfaces | Current bounded correction profile, contradiction-aware correction state, accepted-resolution ledger, and correction-explanation / solve-unlock shell are live above the landed `Phase 6R-O`, `Phase 6R-P`, `Phase 6R-Q`, `Phase 6R-R`, and `Phase 6R-S` seams. Bundled-asset, generalized solver-backend, and broad playback/runtime widening remain deferred. |
### 3. Training, coaching, and progression cockpit
@ -187,7 +188,7 @@ repo.
| Tiling topology and geometry-family contract | Implemented now | landed `MagicTile` packet | New bounded widening is live. |
| Dedicated `120-cell` family runtime profile and persistence boundary | Implemented now | landed `Magic120Cell` packet | Current bounded family-specific widening is live beneath the retained `Hyperspeedcube` runtime anchor. |
| Dedicated `5D` family runtime profile and persistence boundary | Implemented now | landed `MagicCube5D` packet | Current bounded family-specific widening is live beneath the retained `Hyperspeedcube` runtime anchor. |
| Transform-aware macro remapping and broad non-Euclidean interaction shell | Deep-source grounded retained | `MagicTile` retained remainder | Not yet promoted into live broad interaction ownership. |
| Transform-aware macro remapping and broad non-Euclidean interaction shell | Deep-source grounded retained | `MagicTile` retained remainder | Transform-aware macro remapping is the current next bounded retained slice; broad interaction ownership is still deferred. |
### 7. Speech input and voice sidecars

View file

@ -135,9 +135,13 @@ Canonical discovery surfaces for roadmap interpretation:
correction/explanation control pass is now consumed
- the bounded permissive `Phase 6R-R` shared classic-cube recognition multi-face
correction/explanation shell packet is now landed in current code
- the current next bounded move is a source-backed follow-on control pass for the retained
classic-cube recognition correction/explanation shell remainder, while keeping both the
`twistysim.min.js` and `arial-unicode-ms.ttf` redistribution guards explicit
- the generic source-backed `Phase 6R-S` shared classic-cube correction-resolution closure
control pass is now consumed
- the bounded permissive `Phase 6R-S` shared classic-cube correction-resolution closure packet is
now landed in current code
- the current next bounded move is a source-backed `Phase 6R-T` `roice3/MagicTile`
transform-aware macro remapping preparation/control pass, while keeping both classic-cube
bundled-asset guards explicit
- the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
waiting on another first-party packet
@ -199,15 +203,14 @@ 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 follow-on control pass for the retained classic-cube
recognition correction/explanation shell remainder shared across `kkoomen/qbr` and
`vivaansinghvi07/rubix-cube-solver`.
The next bounded move is a source-backed `Phase 6R-T` `roice3/MagicTile`
transform-aware macro remapping preparation/control pass.
Queue interpretation after that control pass:
- then continue with the retained repo-row queue
- highest current repo-row queue pressure sits in:
- the retained classic-cube recognition shell set
- the retained `MagicTile` transform-aware macro remapping remainder
- `HactarCE/Hyperspeedcube` is now closed for the currently justified retained row:
- landed:
- puzzle catalog contract
@ -257,11 +260,13 @@ Queue interpretation after that control pass:
- bounded solve explanation/recommendation stage ladder and recommendation state
- bounded correction profile, contradiction-aware correction state, and
correction-explanation shell routing
- correction-resolution ledger, manual-review acceptance, and solve-guidance reopen/unlock
semantics
- still deferred:
- retained correction/explanation shell remainder beyond the landed
correction-state slice
- bundled `twistysim.min.js` redistribution
- after the landed `Phase 6R-R` implementation packet, keep the future legal sequencing guards
- generalized solver backend ownership
- broad playback-runtime ownership
- after the landed `Phase 6R-S` implementation packet, keep the future legal sequencing guards
visible:
- `vivaansinghvi07/rubix-cube-solver`
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing
@ -269,10 +274,12 @@ Queue interpretation after that control pass:
- `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
- `Phase 6R-R`
- `Phase 6R-S`
- keep `vivaansinghvi07/rubix-cube-solver` as the retained correction/explanation-semantic
owner and preserve `kkoomen/qbr` as the retained capture-state and re-scan adjunct without
widening into either bundled asset family or broad playback/runtime ownership
reopening the shared correction shell unless a narrower first-party gap is proven above the
landed resolution-closure seam or widening into either bundled asset family or broad
playback/runtime ownership
- `roice3/MagicTile` remains a partially landed row rather than a closed row:
- landed:
- tiling topology and geometry-family contract
@ -337,11 +344,11 @@ Queue interpretation after that control pass:
- viseme / gesture runtime integration
- broad assistant-platform scope
- the next queue shape is now:
- retained classic-cube recognition correction/explanation shell remainder assessment
- retained `MagicTile` transform-aware macro remapping assessment
- the next bounded move should stay narrow:
- a source-backed follow-on control pass before any widening into bundled `twistysim.min.js`
redistribution, generalized solver backend ownership, any `qbr` multilingual/font-shipping
path, or broad playback-runtime ownership
- a source-backed `Phase 6R-T` control pass before any widening into broad non-Euclidean
interaction shell, WinForms/OpenTK host ownership, bundled `twistysim.min.js`
redistribution, or any `qbr` multilingual/font-shipping path
- keep the speech-input / voice sidecar legal sequencing guard visible:
- keep code-license judgments separate from model, voice, and payload-license review
- keep the provider-neutral speech-lane guard visible: