Harden validation, tooling, and public product surfaces

This commit is contained in:
axiomlogicnexus 2026-06-23 09:22:54 +00:00
parent 9c93020c69
commit a1d40d1232
29 changed files with 7020 additions and 5015 deletions

2
.gitignore vendored
View file

@ -13,6 +13,8 @@ website/dist/
website/node_modules/ website/node_modules/
website/server/node_modules/ website/server/node_modules/
website/server/data/ website/server/data/
tools/sentrux/bin/*
!tools/sentrux/bin/.gitkeep
# Sensitive or reference-only material. # Sensitive or reference-only material.
docs/refs/ docs/refs/

View file

@ -0,0 +1,968 @@
#include "HyperTwistRecognition/HyperTwistRecognitionTypes.h"
namespace HyperTwistRecognitionTypesInternal
{
bool AreAllStringsPopulated(const TArray<FString>& Values)
{
for (const FString& Value : Values)
{
if (Value.IsEmpty())
{
return false;
}
}
return true;
}
template <typename ItemType>
bool AreAllItemsStructurallyValid(const TArray<ItemType>& Items)
{
for (const ItemType& Item : Items)
{
if (!Item.IsStructurallyValid())
{
return false;
}
}
return true;
}
}
bool FHyperTwistSpeechNativeCaptureRouteShellProfile::IsStructurallyValid() const
{
return !NativeCaptureRouteShellProfileId.IsEmpty()
&& !ShellKind.IsEmpty()
&& !OwnershipSummarySurfaceMode.IsEmpty()
&& !PreparationSurfaceMode.IsEmpty()
&& !SessionReopenSurfaceMode.IsEmpty()
&& !RouteInspectActionId.IsEmpty()
&& !PreparationRetryActionId.IsEmpty()
&& !SessionReopenActionId.IsEmpty()
&& !PermissionDependencyActionId.IsEmpty()
&& Panels.Num() > 0
&& ActionBindings.Num() > 0
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Panels)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(ActionBindings);
}
bool FHyperTwistSpeechNativeCaptureRouteShellState::IsStructurallyValid() const
{
if (NativeCaptureRouteShellProfileId.IsEmpty()
|| NativeCaptureRouteWorkflowProfileId.IsEmpty()
|| ActiveProviderProfileId.IsEmpty()
|| ActiveServiceLaneId.IsEmpty()
|| WorkflowStateId.IsEmpty()
|| CaptureRouteStateId.IsEmpty()
|| PermissionStateId.IsEmpty()
|| LatestSourceKind.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
if (ActiveIssueCount != Issues.Num())
{
return false;
}
return (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Entries)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Issues)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(AvailableActionIds);
}
bool FHyperTwistSpeechUsageCostDashboardShellProfile::IsStructurallyValid() const
{
return !DashboardShellProfileId.IsEmpty()
&& !ShellKind.IsEmpty()
&& !UsageSurfaceMode.IsEmpty()
&& !CostSurfaceMode.IsEmpty()
&& !EscalationBannerMode.IsEmpty()
&& !RefreshActionId.IsEmpty()
&& !RouteInspectActionId.IsEmpty()
&& !BudgetInspectActionId.IsEmpty()
&& Panels.Num() > 0
&& ActionBindings.Num() > 0
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Panels)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(ActionBindings);
}
bool FHyperTwistSpeechUsageCostDashboardShellState::IsStructurallyValid() const
{
if (DashboardShellProfileId.IsEmpty()
|| ActiveProviderProfileId.IsEmpty()
|| ActiveProviderDisplayLabel.IsEmpty()
|| ActiveServiceLaneId.IsEmpty()
|| ActiveRouteStateId.IsEmpty()
|| UsageAggregationWindowId.IsEmpty()
|| CostAggregationWindowId.IsEmpty()
|| UsageMeterKind.IsEmpty()
|| CurrencyCode.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| LatestUsageEventId.IsEmpty()
|| LatestCostEventId.IsEmpty()
|| LatestQuotaStateId.IsEmpty()
|| LatestRouteDecisionId.IsEmpty()
|| DisplayedUsageQuantity < 0.0f
|| DisplayedEstimatedCostUsd < 0.0f
|| DisplayedReportedCostUsd < 0.0f
|| RemainingEstimatedSpendUsd < 0.0f
|| RemainingRequestCount < 0
|| RemainingAudioSeconds < 0
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
if (ActiveIssueCount != Issues.Num())
{
return false;
}
return (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Issues)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(AvailableActionIds);
}
bool FHyperTwistVisionShellProfile::IsStructurallyValid() const
{
return !ShellProfileId.IsEmpty()
&& !ShellKind.IsEmpty()
&& !DefaultLocaleCode.IsEmpty()
&& !FontReviewProfileId.IsEmpty()
&& FontReviewProfileDefinition.IsStructurallyValid()
&& SupportedFontReviews.Num() > 0
&& SupportedLocales.Num() > 0
&& Panels.Num() > 0
&& ActionBindings.Num() > 0
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(SupportedLocales)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(SupportedFontReviews)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Panels)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(ActionBindings);
}
bool FHyperTwistVisionSolveExplanationProfile::IsStructurallyValid() const
{
return !ExplanationProfileId.IsEmpty()
&& !PuzzleId.IsEmpty()
&& !RecommendationMode.IsEmpty()
&& !LocaleGuidanceMode.IsEmpty()
&& !DefaultLocaleCode.IsEmpty()
&& !StartingOrientationHint.IsEmpty()
&& !FontReviewProfileId.IsEmpty()
&& FontReviewProfileDefinition.IsStructurallyValid()
&& SupportedFontReviews.Num() > 0
&& SupportedLocales.Num() > 0
&& Steps.Num() > 0
&& ActionBindings.Num() > 0
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(SupportedLocales)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(SupportedFontReviews)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Steps)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(ActionBindings);
}
bool FHyperTwistVisionCorrectionState::IsStructurallyValid() const
{
if (CorrectionProfileId.IsEmpty()
|| MissingFaceCount < 0
|| ContradictionCount < 0
|| ContradictionCount != Contradictions.Num()
|| ResolvedCorrectionCount < 0
|| ResolvedCorrectionCount != ResolutionLedger.Num())
{
return false;
}
if (bCorrectionRequired
&& (ActiveTargetFaceId.IsEmpty()
|| !ActiveTarget.IsStructurallyValid()
|| ActiveTargetFaceId != ActiveTarget.FaceId))
{
return false;
}
return HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(PendingTargets)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Contradictions)
&& (LastResolution.ResolutionId.IsEmpty() || LastResolution.IsStructurallyValid())
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(ResolutionLedger);
}
bool FHyperTwistSpeechProviderRoutingPolicy::IsStructurallyValid() const
{
return !ProviderRoutingPolicyId.IsEmpty()
&& !PolicyKind.IsEmpty()
&& !RouteSelectionMode.IsEmpty()
&& !FallbackMode.IsEmpty()
&& !WorkflowPolicyState.IsEmpty()
&& !FailureEscalationMode.IsEmpty()
&& !UserOverridePosture.IsEmpty()
&& EligibleProviderClasses.Num() > 0
&& EligibleEndpointClasses.Num() > 0
&& RequiredCapabilityFlags.Num() > 0
&& SupportedTaskKinds.Num() > 0
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(EligibleProviderClasses)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(EligibleEndpointClasses)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(RequiredCapabilityFlags)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(SupportedTaskKinds);
}
namespace HyperTwistRecognitionTypeValidation
{
template <typename TStruct>
bool AreStructurallyValidEntries(const TArray<TStruct>& Values)
{
return HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Values);
}
bool AreNonEmptyStrings(const TArray<FString>& Values)
{
return HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(Values);
}
bool AreSpeechModelPayloadsValid(const TArray<FHyperTwistSpeechModelPayloadDescriptor>& Payloads)
{
return AreStructurallyValidEntries(Payloads);
}
bool HasValidSpeechSessionCore(const FHyperTwistSpeechSessionConfig& Session)
{
return !Session.SessionId.IsEmpty()
&& !Session.ListeningContractId.IsEmpty()
&& !Session.InputRouteId.IsEmpty()
&& !Session.AudioEncoding.IsEmpty()
&& Session.SampleRateHz > 0
&& Session.ChannelCount > 0
&& !Session.TaskKind.IsEmpty()
&& Session.RequiredModelPayloads.Num() > 0
&& Session.VadPolicy.IsStructurallyValid()
&& Session.OrchestrationProfile.IsStructurallyValid();
}
bool HasValidSpeechSessionProfiles(const FHyperTwistSpeechSessionConfig& Session)
{
return !Session.MicrophoneShellProfileId.IsEmpty()
&& Session.MicrophoneShellProfileDefinition.IsStructurallyValid()
&& !Session.DevicePermissionWorkflowProfileId.IsEmpty()
&& Session.DevicePermissionWorkflowProfileDefinition.IsStructurallyValid()
&& !Session.NativeCaptureRouteWorkflowProfileId.IsEmpty()
&& Session.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid()
&& !Session.NativeCaptureRouteShellProfileId.IsEmpty()
&& Session.NativeCaptureRouteShellProfileDefinition.IsStructurallyValid()
&& !Session.ExternalDictationShellProfileId.IsEmpty()
&& Session.ExternalDictationShellProfileDefinition.IsStructurallyValid()
&& !Session.ProviderProfileId.IsEmpty()
&& Session.ProviderProfileDefinition.IsStructurallyValid()
&& !Session.ByokCustodyProfileId.IsEmpty()
&& Session.ByokCustodyProfileDefinition.IsStructurallyValid()
&& !Session.ProviderRoutingPolicyId.IsEmpty()
&& Session.ProviderRoutingPolicyDefinition.IsStructurallyValid()
&& Session.ProviderRouteDecision.IsStructurallyValid()
&& !Session.UsageCostAccountingProfileId.IsEmpty()
&& Session.UsageCostAccountingProfileDefinition.IsStructurallyValid()
&& Session.UsageEventTemplate.IsStructurallyValid()
&& Session.CostEventTemplate.IsStructurallyValid()
&& !Session.UsageCostDashboardShellProfileId.IsEmpty()
&& Session.UsageCostDashboardShellProfileDefinition.IsStructurallyValid()
&& !Session.UsageCostHistoryExportShellProfileId.IsEmpty()
&& Session.UsageCostHistoryExportShellProfileDefinition.IsStructurallyValid()
&& !Session.ProviderReceiptReviewShellProfileId.IsEmpty()
&& Session.ProviderReceiptReviewShellProfileDefinition.IsStructurallyValid()
&& !Session.ProviderBillingSettlementShellProfileId.IsEmpty()
&& Session.ProviderBillingSettlementShellProfileDefinition.IsStructurallyValid()
&& !Session.ProviderSettlementExceptionShellProfileId.IsEmpty()
&& Session.ProviderSettlementExceptionShellProfileDefinition.IsStructurallyValid()
&& !Session.PayloadCustodyProfileId.IsEmpty()
&& Session.PayloadCustodyProfileDefinition.IsStructurallyValid();
}
bool HasValidTranscriptCore(const FHyperTwistSpeechTranscriptResult& Transcript)
{
return !Transcript.SessionId.IsEmpty()
&& !Transcript.UtteranceId.IsEmpty()
&& !Transcript.TaskKind.IsEmpty()
&& (!Transcript.TranscriptText.IsEmpty() || Transcript.Segments.Num() > 0)
&& !Transcript.OrchestrationProfileId.IsEmpty()
&& !Transcript.ServiceLaneId.IsEmpty()
&& Transcript.RequestedBatchSize > 0
&& Transcript.ProcessedClipCount > 0
&& Transcript.AppliedBatchCollectionWindowMs > 0
&& Transcript.LanguageProbability >= 0.0f
&& !Transcript.AppliedPromptRoutingModeId.IsEmpty()
&& !Transcript.AppliedRetrievalContextLaneId.IsEmpty();
}
bool AreTranscriptSegmentsValid(const TArray<FHyperTwistSpeechTranscriptSegment>& Segments)
{
return AreStructurallyValidEntries(Segments);
}
bool HasValidSpeechServiceHealthCore(const FHyperTwistSpeechServiceHealth& Health)
{
return !Health.ProviderLabel.IsEmpty()
&& !Health.ServiceVersion.IsEmpty()
&& !Health.ProviderProfileId.IsEmpty()
&& Health.ProviderProfileDefinition.IsStructurallyValid()
&& !Health.ByokCustodyProfileId.IsEmpty()
&& Health.ByokCustodyProfileDefinition.IsStructurallyValid()
&& !Health.ProviderRoutingPolicyId.IsEmpty()
&& Health.ProviderRoutingPolicyDefinition.IsStructurallyValid()
&& Health.ProviderRouteDecision.IsStructurallyValid()
&& !Health.UsageCostAccountingProfileId.IsEmpty()
&& Health.UsageCostAccountingProfileDefinition.IsStructurallyValid()
&& Health.LatestUsageEvent.IsStructurallyValid()
&& Health.LatestCostEvent.IsStructurallyValid()
&& Health.QuotaRateLimitState.IsStructurallyValid()
&& !Health.PayloadCustodyProfileId.IsEmpty()
&& Health.PayloadCustodyProfileDefinition.IsStructurallyValid();
}
bool HasValidExternalDictationShellProfileCore(
const FHyperTwistSpeechExternalDictationShellProfile& Profile)
{
return !Profile.ExternalDictationShellProfileId.IsEmpty()
&& !Profile.ShellKind.IsEmpty()
&& !Profile.TranscriptHistorySurfaceMode.IsEmpty()
&& !Profile.OutputRoutingSurfaceMode.IsEmpty()
&& !Profile.PostProcessOverlaySurfaceMode.IsEmpty()
&& !Profile.GlobalHotkeySurfaceMode.IsEmpty()
&& !Profile.InputDeviceSurfaceMode.IsEmpty()
&& !Profile.OutputDeviceSurfaceMode.IsEmpty()
&& !Profile.MuteSurfaceMode.IsEmpty()
&& !Profile.MicrophoneModeSurfaceMode.IsEmpty()
&& !Profile.LocalModelCatalogSurfaceMode.IsEmpty()
&& !Profile.ModelIntegritySurfaceMode.IsEmpty()
&& !Profile.ModelUnloadSurfaceMode.IsEmpty()
&& !Profile.StartCaptureActionId.IsEmpty()
&& !Profile.CancelCaptureActionId.IsEmpty()
&& !Profile.CycleOutputRouteActionId.IsEmpty()
&& !Profile.CopyTranscriptActionId.IsEmpty()
&& !Profile.PasteTranscriptActionId.IsEmpty()
&& !Profile.ScriptDispatchActionId.IsEmpty()
&& !Profile.TogglePostProcessOverlayActionId.IsEmpty()
&& !Profile.ReopenSpeechSessionActionId.IsEmpty()
&& !Profile.RouteInspectActionId.IsEmpty()
&& !Profile.InspectGlobalHotkeyActionId.IsEmpty()
&& !Profile.CycleInputDeviceActionId.IsEmpty()
&& !Profile.CycleOutputDeviceActionId.IsEmpty()
&& !Profile.ToggleMuteWhileRecordingActionId.IsEmpty()
&& !Profile.ToggleMicrophoneModeActionId.IsEmpty()
&& !Profile.InspectLocalModelCatalogActionId.IsEmpty()
&& !Profile.ReviewLocalModelIntegrityActionId.IsEmpty()
&& !Profile.ReviewLocalModelUnloadPolicyActionId.IsEmpty()
&& !Profile.PrimaryHotkeyBindingLabel.IsEmpty()
&& !Profile.PostProcessHotkeyBindingLabel.IsEmpty()
&& Profile.RetainedHistoryEntryLimit > 0
&& Profile.Panels.Num() > 0
&& Profile.ActionBindings.Num() > 0;
}
bool HasValidExternalDictationShellStateCore(const FHyperTwistSpeechExternalDictationShellState& State)
{
return !State.ExternalDictationShellProfileId.IsEmpty()
&& !State.ActiveProviderProfileId.IsEmpty()
&& !State.ActiveServiceLaneId.IsEmpty()
&& !State.SelectedOutputRouteId.IsEmpty()
&& !State.TranscriptHistorySurfaceModeId.IsEmpty()
&& !State.OutputRoutingSurfaceModeId.IsEmpty()
&& !State.PostProcessOverlaySurfaceModeId.IsEmpty()
&& !State.GlobalHotkeySurfaceModeId.IsEmpty()
&& !State.InputDeviceSurfaceModeId.IsEmpty()
&& !State.OutputDeviceSurfaceModeId.IsEmpty()
&& !State.MuteSurfaceModeId.IsEmpty()
&& !State.MicrophoneModeSurfaceModeId.IsEmpty()
&& !State.LocalModelCatalogSurfaceModeId.IsEmpty()
&& !State.ModelIntegritySurfaceModeId.IsEmpty()
&& !State.ModelUnloadSurfaceModeId.IsEmpty()
&& !State.LatestSourceKind.IsEmpty()
&& !State.StatusLine.IsEmpty()
&& !State.DetailLine.IsEmpty()
&& !State.PrimaryHotkeyBindingLabel.IsEmpty()
&& !State.PostProcessHotkeyBindingLabel.IsEmpty()
&& !State.SelectedInputDeviceId.IsEmpty()
&& !State.SelectedInputDeviceLabel.IsEmpty()
&& !State.SelectedOutputDeviceId.IsEmpty()
&& !State.SelectedOutputDeviceLabel.IsEmpty()
&& !State.MicrophoneModeId.IsEmpty()
&& !State.PayloadCustodyProfileId.IsEmpty()
&& !State.SelectedPrimaryPayloadId.IsEmpty()
&& State.HistoryEntryCount >= 0
&& State.PostProcessedEntryCount >= 0
&& State.SavedEntryCount >= 0
&& State.ModelCatalogEntryCount >= 0
&& State.OptionalModelCatalogEntryCount >= 0
&& State.DownloadDeferredModelEntryCount >= 0
&& State.IntegrityReviewRequiredEntryCount >= 0
&& State.ActiveIssueCount >= 0
&& State.OutputRouteOptions.Num() > 0
&& State.InputDeviceOptions.Num() > 0
&& State.OutputDeviceOptions.Num() > 0
&& State.ModelCatalogEntries.Num() > 0
&& State.AvailableActionIds.Num() > 0;
}
bool HasConsistentExternalDictationShellStateCounts(const FHyperTwistSpeechExternalDictationShellState& State)
{
return State.HistoryEntryCount == State.HistoryEntries.Num()
&& State.PostProcessedEntryCount <= State.HistoryEntryCount
&& State.SavedEntryCount <= State.HistoryEntryCount
&& State.ModelCatalogEntryCount == State.ModelCatalogEntries.Num()
&& State.OptionalModelCatalogEntryCount <= State.ModelCatalogEntryCount
&& State.DownloadDeferredModelEntryCount <= State.ModelCatalogEntryCount
&& State.IntegrityReviewRequiredEntryCount <= State.ModelCatalogEntryCount;
}
bool HasConsistentExternalDictationShellStateLatestEntry(const FHyperTwistSpeechExternalDictationShellState& State)
{
return State.HistoryEntryCount <= 0
|| (!State.LatestHistoryEntryId.IsEmpty() && !State.LatestTranscriptText.IsEmpty());
}
bool HasConsistentExternalDictationShellStateActiveIssue(const FHyperTwistSpeechExternalDictationShellState& State)
{
return State.ActiveIssueCount == State.Issues.Num()
&& (State.ActiveIssueCount <= 0 || State.ActiveIssue.IsStructurallyValid());
}
}
bool FHyperTwistSpeechUsageCostHistoryExportShellState::IsStructurallyValid() const
{
if (HistoryExportShellProfileId.IsEmpty()
|| ActiveProviderProfileId.IsEmpty()
|| ActiveProviderDisplayLabel.IsEmpty()
|| ActiveServiceLaneId.IsEmpty()
|| SelectedHistoryWindowId.IsEmpty()
|| ExportPreviewFormatId.IsEmpty()
|| LatestSourceKind.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| HistoryEntryCount < 0
|| EstimateOnlyEntryCount < 0
|| ProviderReceiptEntryCount < 0
|| TotalUsageQuantity < 0.0f
|| TotalEstimatedCostUsd < 0.0f
|| TotalReportedCostUsd < 0.0f
|| LowestRemainingEstimatedSpendUsd < 0.0f
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
return HistoryEntryCount == HistoryEntries.Num()
&& (HistoryEntryCount <= 0 || !LatestHistoryEntryId.IsEmpty())
&& (!bExportPreviewReady || !ExportPreviewText.IsEmpty())
&& ActiveIssueCount == Issues.Num()
&& (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(Issues)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(HistoryEntries)
&& HyperTwistRecognitionTypeValidation::AreNonEmptyStrings(AvailableActionIds);
}
bool FHyperTwistSpeechProviderReceiptReviewShellState::IsStructurallyValid() const
{
if (ReceiptReviewShellProfileId.IsEmpty()
|| ActiveProviderProfileId.IsEmpty()
|| ActiveProviderDisplayLabel.IsEmpty()
|| ActiveServiceLaneId.IsEmpty()
|| SelectedChargeWindowId.IsEmpty()
|| PostedChargeInspectionModeId.IsEmpty()
|| LatestSourceKind.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| ReceiptEntryCount < 0
|| ProviderPostedChargeCount < 0
|| EstimateOnlyEntryCount < 0
|| VarianceReviewEntryCount < 0
|| TotalEstimatedCostUsd < 0.0f
|| TotalReportedCostUsd < 0.0f
|| TotalAbsoluteVarianceUsd < 0.0f
|| HighestAbsoluteVarianceUsd < 0.0f
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
return ReceiptEntryCount == ReceiptEntries.Num()
&& ProviderPostedChargeCount <= ReceiptEntryCount
&& EstimateOnlyEntryCount <= ReceiptEntryCount
&& VarianceReviewEntryCount <= ReceiptEntryCount
&& (ReceiptEntryCount <= 0 || !LatestReceiptEntryId.IsEmpty())
&& (!bReceiptSummaryReady || !ReceiptSummaryText.IsEmpty())
&& ActiveIssueCount == Issues.Num()
&& (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(Issues)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(ReceiptEntries)
&& HyperTwistRecognitionTypeValidation::AreNonEmptyStrings(AvailableActionIds);
}
bool FHyperTwistSpeechProviderBillingSettlementShellState::IsStructurallyValid() const
{
if (BillingSettlementShellProfileId.IsEmpty()
|| ActiveProviderProfileId.IsEmpty()
|| ActiveProviderDisplayLabel.IsEmpty()
|| ActiveServiceLaneId.IsEmpty()
|| SelectedSettlementWindowId.IsEmpty()
|| SettlementSurfaceModeId.IsEmpty()
|| InvoiceReconciliationModeId.IsEmpty()
|| LatestSourceKind.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| SettlementEntryCount < 0
|| ProviderPostedChargeCount < 0
|| ReconciliationReadyEntryCount < 0
|| EstimateOnlyEntryCount < 0
|| ManualReviewEntryCount < 0
|| BlockedSettlementEntryCount < 0
|| TotalEstimatedCostUsd < 0.0f
|| TotalReportedCostUsd < 0.0f
|| TotalSettlementDeltaUsd < 0.0f
|| HighestSettlementDeltaUsd < 0.0f
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
return SettlementEntryCount == SettlementEntries.Num()
&& ProviderPostedChargeCount <= SettlementEntryCount
&& ReconciliationReadyEntryCount <= SettlementEntryCount
&& EstimateOnlyEntryCount <= SettlementEntryCount
&& ManualReviewEntryCount <= SettlementEntryCount
&& BlockedSettlementEntryCount <= SettlementEntryCount
&& (SettlementEntryCount <= 0 || !LatestSettlementEntryId.IsEmpty())
&& (!bSettlementSummaryReady || !SettlementSummaryText.IsEmpty())
&& ActiveIssueCount == Issues.Num()
&& (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(Issues)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(SettlementEntries)
&& HyperTwistRecognitionTypeValidation::AreNonEmptyStrings(AvailableActionIds);
}
bool FHyperTwistSpeechProviderSettlementExceptionShellState::IsStructurallyValid() const
{
if (SettlementExceptionShellProfileId.IsEmpty()
|| ActiveProviderProfileId.IsEmpty()
|| ActiveProviderDisplayLabel.IsEmpty()
|| ActiveServiceLaneId.IsEmpty()
|| SelectedSettlementWindowId.IsEmpty()
|| ExceptionSurfaceModeId.IsEmpty()
|| ExternalPortalHandoffModeId.IsEmpty()
|| LatestSourceKind.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| ExceptionEntryCount < 0
|| ExternalPortalHandoffReadyEntryCount < 0
|| PendingChargeEntryCount < 0
|| ManualReviewEntryCount < 0
|| RouteDegradedEntryCount < 0
|| BlockedSettlementEntryCount < 0
|| TotalEstimatedCostUsd < 0.0f
|| TotalReportedCostUsd < 0.0f
|| TotalSettlementDeltaUsd < 0.0f
|| HighestSettlementDeltaUsd < 0.0f
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
return ExceptionEntryCount == ExceptionEntries.Num()
&& ExternalPortalHandoffReadyEntryCount <= ExceptionEntryCount
&& PendingChargeEntryCount <= ExceptionEntryCount
&& ManualReviewEntryCount <= ExceptionEntryCount
&& RouteDegradedEntryCount <= ExceptionEntryCount
&& BlockedSettlementEntryCount <= ExceptionEntryCount
&& (ExceptionEntryCount <= 0 || !LatestExceptionEntryId.IsEmpty())
&& (!bExceptionSummaryReady || !ExceptionSummaryText.IsEmpty())
&& ActiveIssueCount == Issues.Num()
&& (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(Issues)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(ExceptionEntries)
&& HyperTwistRecognitionTypeValidation::AreNonEmptyStrings(AvailableActionIds);
}
bool FHyperTwistSpeechExternalDictationShellProfile::IsStructurallyValid() const
{
return HyperTwistRecognitionTypeValidation::HasValidExternalDictationShellProfileCore(*this)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(Panels)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(ActionBindings);
}
bool FHyperTwistSpeechExternalDictationShellState::IsStructurallyValid() const
{
return HyperTwistRecognitionTypeValidation::HasValidExternalDictationShellStateCore(*this)
&& HyperTwistRecognitionTypeValidation::HasConsistentExternalDictationShellStateCounts(*this)
&& HyperTwistRecognitionTypeValidation::HasConsistentExternalDictationShellStateLatestEntry(*this)
&& HyperTwistRecognitionTypeValidation::HasConsistentExternalDictationShellStateActiveIssue(*this)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(Issues)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(HistoryEntries)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(OutputRouteOptions)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(InputDeviceOptions)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(OutputDeviceOptions)
&& HyperTwistRecognitionTypeValidation::AreStructurallyValidEntries(ModelCatalogEntries)
&& HyperTwistRecognitionTypeValidation::AreNonEmptyStrings(AvailableActionIds);
}
bool FHyperTwistSpeechSessionConfig::IsStructurallyValid() const
{
return HyperTwistRecognitionTypeValidation::HasValidSpeechSessionCore(*this)
&& HyperTwistRecognitionTypeValidation::HasValidSpeechSessionProfiles(*this)
&& HyperTwistRecognitionTypeValidation::AreSpeechModelPayloadsValid(RequiredModelPayloads);
}
bool FHyperTwistSpeechTranscriptResult::IsStructurallyValid() const
{
if (!HyperTwistRecognitionTypeValidation::HasValidTranscriptCore(*this)
|| RetrievedHintCount < 0)
{
return false;
}
if (bUsedRetrievedHintAugmentation && RetrievedHintCount <= 0)
{
return false;
}
return RetrievedHintCount == AppliedRetrievedHints.Num()
&& HyperTwistRecognitionTypeValidation::AreTranscriptSegmentsValid(Segments)
&& HyperTwistRecognitionTypeValidation::AreNonEmptyStrings(AppliedRetrievedHints);
}
bool FHyperTwistSpeechServiceHealth::IsStructurallyValid() const
{
return HyperTwistRecognitionTypeValidation::HasValidSpeechServiceHealthCore(*this)
&& HyperTwistRecognitionTypeValidation::AreNonEmptyStrings(SupportedModelPayloadIds)
&& HyperTwistRecognitionTypeValidation::AreSpeechModelPayloadsValid(SupportedModelPayloads);
}
bool FHyperTwistVoiceAssetDescriptor::IsStructurallyValid() const
{
return !VoiceAssetId.IsEmpty()
&& !VoiceProfileId.IsEmpty()
&& !ArtifactName.IsEmpty()
&& !RelativePathHint.IsEmpty()
&& !SourceDocumentPath.IsEmpty()
&& !ReviewDocumentPath.IsEmpty()
&& !AcquisitionMode.IsEmpty()
&& !ReviewPosture.IsEmpty()
&& ApproximateSizeMiB > 0;
}
bool FHyperTwistVoiceAssetReviewProfile::IsStructurallyValid() const
{
return !VoiceAssetReviewProfileId.IsEmpty()
&& !VoiceAssetReviewPosture.IsEmpty()
&& !ShippingPosture.IsEmpty()
&& !DownloadWorkflowPosture.IsEmpty()
&& !ProvisioningPosture.IsEmpty()
&& !CodeLicenseBoundary.IsEmpty();
}
bool FHyperTwistVoiceModelReviewDescriptor::IsStructurallyValid() const
{
return !VoiceModelReviewId.IsEmpty()
&& !ModelBindingId.IsEmpty()
&& !VocoderBindingId.IsEmpty()
&& !VoiceProfileId.IsEmpty()
&& !RegistryReferencePath.IsEmpty()
&& !ReviewDocumentPath.IsEmpty()
&& !ModelLicensePosture.IsEmpty()
&& !PayloadLicensePosture.IsEmpty()
&& !AcquisitionMode.IsEmpty();
}
bool FHyperTwistVoiceModelReviewProfile::IsStructurallyValid() const
{
return !VoiceModelReviewProfileId.IsEmpty()
&& !ReviewPosture.IsEmpty()
&& !ShippingPosture.IsEmpty()
&& !DownloadWorkflowPosture.IsEmpty()
&& !CodeLicenseBoundary.IsEmpty();
}
bool FHyperTwistVoiceProfileSummary::IsStructurallyValid() const
{
return !VoiceProfileId.IsEmpty()
&& !VoiceName.IsEmpty()
&& !LanguageCode.IsEmpty()
&& !LanguageFamily.IsEmpty()
&& !RegionCode.IsEmpty()
&& !LanguageNameEnglish.IsEmpty()
&& !Quality.IsEmpty()
&& NumSpeakers > 0;
}
bool FHyperTwistVoiceProfileCatalog::IsStructurallyValid() const
{
return Profiles.Num() > 0
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Profiles);
}
bool FHyperTwistNarrationOrchestrationProfile::IsStructurallyValid() const
{
return !OrchestrationProfileId.IsEmpty()
&& !ServiceLaneId.IsEmpty()
&& !ModelBindingId.IsEmpty()
&& !VocoderBindingId.IsEmpty()
&& !DefaultSpeakerProfileId.IsEmpty();
}
bool FHyperTwistNarrationSynthesisRequest::IsStructurallyValid() const
{
return !RequestId.IsEmpty()
&& !NarrationContractId.IsEmpty()
&& !ServiceLaneId.IsEmpty()
&& !OutputRouteId.IsEmpty()
&& !VoiceProfileId.IsEmpty()
&& !LanguageCode.IsEmpty()
&& OrchestrationProfile.IsStructurallyValid()
&& ServiceLaneId.Equals(OrchestrationProfile.ServiceLaneId, ESearchCase::CaseSensitive)
&& !ScriptText.IsEmpty()
&& !AudioEncodingHint.IsEmpty()
&& LengthScale > 0.0f
&& NoiseScale >= 0.0f
&& NoiseW >= 0.0f
&& SentenceSilenceSeconds >= 0.0f;
}
bool FHyperTwistNarrationSynthesisResult::IsStructurallyValid() const
{
return !RequestId.IsEmpty()
&& !NarrationContractId.IsEmpty()
&& !ServiceLaneId.IsEmpty()
&& !OutputRouteId.IsEmpty()
&& !VoiceProfileId.IsEmpty()
&& !LanguageCode.IsEmpty()
&& !OrchestrationProfileId.IsEmpty()
&& !ModelBindingId.IsEmpty()
&& !VocoderBindingId.IsEmpty()
&& !SubtitleText.IsEmpty()
&& !AudioEncoding.IsEmpty()
&& SampleRateHz > 0
&& ChannelCount > 0
&& DurationMs >= 0
&& AudioBytes.Num() > 0
&& AppliedLengthScale > 0.0f
&& AppliedNoiseScale >= 0.0f
&& AppliedNoiseW >= 0.0f
&& AppliedSentenceSilenceSeconds >= 0.0f;
}
bool FHyperTwistVoiceServiceHealth::IsStructurallyValid() const
{
if (ProviderLabel.IsEmpty() || ServiceVersion.IsEmpty())
{
return false;
}
if ((!VoiceAssetReviewProfileId.IsEmpty()
|| VoiceAssetReviewProfileDefinition.IsStructurallyValid()
|| SupportedVoiceAssetIds.Num() > 0
|| SupportedVoiceAssets.Num() > 0)
&& (VoiceAssetReviewProfileId.IsEmpty()
|| !VoiceAssetReviewProfileDefinition.IsStructurallyValid()
|| SupportedVoiceAssetIds.Num() != SupportedVoiceAssets.Num()))
{
return false;
}
if ((!VoiceModelReviewProfileId.IsEmpty()
|| VoiceModelReviewProfileDefinition.IsStructurallyValid()
|| SupportedVoiceModelReviewIds.Num() > 0
|| SupportedVoiceModelReviews.Num() > 0)
&& (VoiceModelReviewProfileId.IsEmpty()
|| !VoiceModelReviewProfileDefinition.IsStructurallyValid()
|| SupportedVoiceModelReviewIds.Num() != SupportedVoiceModelReviews.Num()))
{
return false;
}
return HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(SupportedVoiceAssetIds)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(SupportedVoiceAssets)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(SupportedVoiceModelReviewIds)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(SupportedVoiceModelReviews);
}
bool FHyperTwistSpeechMicrophoneShellState::IsStructurallyValid() const
{
if (MicrophoneShellProfileId.IsEmpty()
|| CaptureMode.IsEmpty()
|| ListeningContractId.IsEmpty()
|| InputRouteId.IsEmpty()
|| PermissionStateId.IsEmpty()
|| CaptureRouteStateId.IsEmpty()
|| LastLanguageCode.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| StepWindowMs <= 0
|| CaptureWindowMs < StepWindowMs
|| KeepWindowMs < 0
|| KeepWindowMs > CaptureWindowMs
|| SubmittedUtteranceCount < 0
|| FinalTranscriptCount < 0
|| DetectedSpeechStartMs < 0
|| DetectedSpeechEndMs < DetectedSpeechStartMs
|| LastSilenceGapMs < 0
|| VadThreshold <= 0.0f
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
return ActiveIssueCount == Issues.Num()
&& (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Issues)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(AvailableActionIds);
}
bool FHyperTwistSpeechDevicePermissionWorkflowProfile::IsStructurallyValid() const
{
return !DevicePermissionWorkflowProfileId.IsEmpty()
&& !WorkflowKind.IsEmpty()
&& !PermissionContractId.IsEmpty()
&& !SettingsHandoffContractId.IsEmpty()
&& !PermissionRecheckContractId.IsEmpty()
&& !PermissionRequestActionId.IsEmpty()
&& !OpenSettingsActionId.IsEmpty()
&& !PermissionRecheckActionId.IsEmpty()
&& Panels.Num() > 0
&& ActionBindings.Num() > 0
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Panels)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(ActionBindings);
}
bool FHyperTwistSpeechDevicePermissionWorkflowEntry::IsStructurallyValid() const
{
return !EntryId.IsEmpty()
&& !SourceKind.IsEmpty()
&& !WorkflowStateId.IsEmpty()
&& !StatusLine.IsEmpty()
&& !DetailLine.IsEmpty()
&& !RecommendedActionId.IsEmpty();
}
bool FHyperTwistSpeechDevicePermissionWorkflowIssue::IsStructurallyValid() const
{
return !IssueId.IsEmpty()
&& !IssueKind.IsEmpty()
&& !StatusLine.IsEmpty()
&& !DetailLine.IsEmpty()
&& !RecommendedActionId.IsEmpty();
}
bool FHyperTwistSpeechDevicePermissionWorkflowState::IsStructurallyValid() const
{
if (DevicePermissionWorkflowProfileId.IsEmpty()
|| WorkflowStateId.IsEmpty()
|| PermissionStateId.IsEmpty()
|| LatestSourceKind.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| WorkflowEntryCount < 0
|| PermissionRequestEntryCount < 0
|| SettingsHandoffEntryCount < 0
|| PermissionRecheckEntryCount < 0
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
if (WorkflowEntryCount != Entries.Num()
|| ActiveIssueCount != Issues.Num()
|| PermissionRequestEntryCount > WorkflowEntryCount
|| SettingsHandoffEntryCount > WorkflowEntryCount
|| PermissionRecheckEntryCount > WorkflowEntryCount)
{
return false;
}
return (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Entries)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Issues)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(AvailableActionIds);
}
bool FHyperTwistSpeechNativeCaptureRouteWorkflowProfile::IsStructurallyValid() const
{
return !NativeCaptureRouteWorkflowProfileId.IsEmpty()
&& !WorkflowKind.IsEmpty()
&& !OwnershipContractId.IsEmpty()
&& !PreparationContractId.IsEmpty()
&& !SessionReopenContractId.IsEmpty()
&& !RouteInspectActionId.IsEmpty()
&& !PreparationRetryActionId.IsEmpty()
&& !SessionReopenActionId.IsEmpty()
&& !PermissionDependencyActionId.IsEmpty()
&& Panels.Num() > 0
&& ActionBindings.Num() > 0
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Panels)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(ActionBindings);
}
bool FHyperTwistSpeechNativeCaptureRouteWorkflowEntry::IsStructurallyValid() const
{
return !EntryId.IsEmpty()
&& !SourceKind.IsEmpty()
&& !WorkflowStateId.IsEmpty()
&& !CaptureRouteStateId.IsEmpty()
&& !StatusLine.IsEmpty()
&& !DetailLine.IsEmpty()
&& !RecommendedActionId.IsEmpty();
}
bool FHyperTwistSpeechNativeCaptureRouteWorkflowIssue::IsStructurallyValid() const
{
return !IssueId.IsEmpty()
&& !IssueKind.IsEmpty()
&& !StatusLine.IsEmpty()
&& !DetailLine.IsEmpty()
&& !RecommendedActionId.IsEmpty();
}
bool FHyperTwistSpeechNativeCaptureRouteWorkflowState::IsStructurallyValid() const
{
if (NativeCaptureRouteWorkflowProfileId.IsEmpty()
|| WorkflowStateId.IsEmpty()
|| CaptureRouteStateId.IsEmpty()
|| PermissionStateId.IsEmpty()
|| ActiveProviderProfileId.IsEmpty()
|| ActiveServiceLaneId.IsEmpty()
|| LatestSourceKind.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| WorkflowEntryCount < 0
|| PreparationEntryCount < 0
|| RouteRetryEntryCount < 0
|| SessionReopenEntryCount < 0
|| PermissionDependencyEntryCount < 0
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
if (WorkflowEntryCount != Entries.Num()
|| ActiveIssueCount != Issues.Num()
|| PreparationEntryCount > WorkflowEntryCount
|| RouteRetryEntryCount > WorkflowEntryCount
|| SessionReopenEntryCount > WorkflowEntryCount
|| PermissionDependencyEntryCount > WorkflowEntryCount)
{
return false;
}
return (ActiveIssueCount <= 0 || ActiveIssue.IsStructurallyValid())
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Entries)
&& HyperTwistRecognitionTypesInternal::AreAllItemsStructurallyValid(Issues)
&& HyperTwistRecognitionTypesInternal::AreAllStringsPopulated(AvailableActionIds);
}

View file

@ -3647,6 +3647,11 @@ UHyperTwistSkillCoreLibrary::DeriveSkillContinuityResumeState(
} }
else if (SkillId == TEXT("skill/capture-note")) else if (SkillId == TEXT("skill/capture-note"))
{ {
const bool bCanCaptureNotesNow =
KnowledgeNotesState.bUserNotesLaneAvailable
&& !KnowledgeNotesState.UserNotesPosture.bRequiresSeparateOwnerActivation
&& Entry->Status == EHyperTwistSkillStatus::ImplementedNow;
SkillState.InputStateKinds = { SkillState.InputStateKinds = {
TEXT("state/memory-knowledge-notes"), TEXT("state/memory-knowledge-notes"),
TEXT("state/memory-user-notes-posture") TEXT("state/memory-user-notes-posture")
@ -3660,14 +3665,13 @@ UHyperTwistSkillCoreLibrary::DeriveSkillContinuityResumeState(
KnowledgeNotesState.UserNotesPosture.SummaryLine KnowledgeNotesState.UserNotesPosture.SummaryLine
); );
SkillState.AvailableItemCount = SkillState.AvailableItemCount = bCanCaptureNotesNow ? 1 : 0;
KnowledgeNotesState.bUserNotesLaneAvailable ? 1 : 0;
SkillState.BlockingItemCount = SkillState.BlockingItemCount =
KnowledgeNotesState.UserNotesPosture.bRequiresSeparateOwnerActivation ? 1 : 0; KnowledgeNotesState.UserNotesPosture.bRequiresSeparateOwnerActivation ? 1 : 0;
SkillState.bLiveSkill = false; SkillState.bLiveSkill = false;
SkillState.bReadsAuthoritativeStores = SkillState.bReadsAuthoritativeStores =
KnowledgeNotesState.UserNotesPosture.bOwnershipJustified; KnowledgeNotesState.UserNotesPosture.bOwnershipJustified;
SkillState.bAvailableNow = KnowledgeNotesState.bUserNotesLaneAvailable; SkillState.bAvailableNow = bCanCaptureNotesNow;
SkillState.bRequiresOwnerActivation = SkillState.bRequiresOwnerActivation =
KnowledgeNotesState.UserNotesPosture.bRequiresSeparateOwnerActivation; KnowledgeNotesState.UserNotesPosture.bRequiresSeparateOwnerActivation;
SkillState.Summary = SkillState.Summary =

View file

@ -20,6 +20,7 @@ VectorShell.
## Landed HyperTwist-owned entry points ## Landed HyperTwist-owned entry points
- `.sentrux/rules.toml` - `.sentrux/rules.toml`
- `scripts/bootstrap-hypertwist-sentrux.sh`
- `scripts/run-hypertwist-sentrux-source-only.sh` - `scripts/run-hypertwist-sentrux-source-only.sh`
- `scripts/run-hypertwist-gitnexus-analyze.sh` - `scripts/run-hypertwist-gitnexus-analyze.sh`
- `scripts/run-hypertwist-gitnexus-status.sh` - `scripts/run-hypertwist-gitnexus-status.sh`
@ -56,9 +57,16 @@ Resolution order for the analyzer binary is now HyperTwist-owned first:
- `HYPERTWIST_SENTRUX_BINARY` if explicitly provided - `HYPERTWIST_SENTRUX_BINARY` if explicitly provided
- repo-local `./sentrux` or `./sentrux.exe` if present - repo-local `./sentrux` or `./sentrux.exe` if present
- repo-local `tools/sentrux/bin/sentrux` or `tools/sentrux/bin/sentrux.exe`
- `sentrux` on `PATH` - `sentrux` on `PATH`
- the retained local fallback under `/home/dev/src/VectorShell/sentrux` - the retained local fallback under `/home/dev/src/VectorShell/sentrux`
If the repo-local tools path is empty, materialize it with:
```bash
scripts/bootstrap-hypertwist-sentrux.sh
```
Current rules enforce: Current rules enforce:
- no cycles - no cycles
@ -237,6 +245,129 @@ Important nuance from the `2026-06-22` follow-up:
family extraction or multi-header ownership separation for the remaining family extraction or multi-header ownership separation for the remaining
validator clusters rather than more in-place header-local helperization validator clusters rather than more in-place header-local helperization
## `2026-06-23` continuation refresh
Additional same-lane follow-up on `2026-06-23`:
- the remaining inline validator clusters in
`HyperTwistSkillTypes.h` and `HyperTwistRecognitionTypes.h` were then moved
further out of the public headers into dedicated private translation units:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistSkills/HyperTwistSkillTypes.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistRecognitionTypes.cpp`
- a public-website/manual continuation also landed in the same pass so the
docs/resources/support surfaces now explicitly project current keyboard,
higher-dimensional control, and unfinished XR/controller truth instead of
only the browser-versus-desktop topology boundary
- focused website coverage for
`src/__tests__/public-marketing-pages.test.tsx` and
`src/__tests__/protected-app-pages.test.tsx` passed after that public-manual
continuation
- a fresh full website validation pass then stayed green:
- `npm --prefix website test -- --run`
- `37` test files passed
- `137` tests passed
- the same widened public-manual packet also kept the production website build
green under `npm --prefix website run build`
Current highest-signal structural truth after that `2026-06-23` continuation:
- `scripts/run-hypertwist-sentrux-source-only.sh` now reports `Quality: 6070`
- the only remaining reported `sentrux` debt is still:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h:IsStructurallyValid (308 lines)`
- that remaining hit is now more likely a parser/grouping artifact than a new
broad website or browser regression, because the website/manual widening
stayed structurally clean and the real validator ownership was moved out of
the header family rather than expanded inside it
Additional same-lane follow-up later on `2026-06-23`:
- `scripts/run-hypertwist-gitnexus-analyze.sh` re-indexed the bounded
source-only mirror successfully at `16,042` nodes, `37,412` edges,
`643` clusters, and `300` flows
- `scripts/run-hypertwist-gitnexus-status.sh` again reported the bounded
mirror `Status: up-to-date`
- a fresh full website validation rerun stayed green again:
- `npm --prefix website test -- --run`
- `37` test files passed
- `137` tests passed
- duration `4.63s`
- `npm --prefix website run build`
- Vite production build succeeded
- `scripts/run-hypertwist-sentrux-source-only.sh` improved again to
`Quality: 6076`
- one more out-of-header migration moved
`FHyperTwistSpeechExternalDictationShellProfile::IsStructurallyValid()`
into `Private/HyperTwistRecognition/HyperTwistRecognitionTypes.cpp`
- the same remaining reported `sentrux` hit still stayed fixed at
`HyperTwistRecognitionTypes.h:IsStructurallyValid (308 lines)` even after
that migration, which further supports the current interpretation that this
residual is a repeated-name/grouping artifact inside the analyzer rather
than one newly expanded high-risk validator body
- the public-website lane was then refactored into bounded per-page modules so
the website manual/distribution surface is no longer concentrated in one
large `public-pages.tsx` owner
- the public route tree then stopped lazy-loading the old barrel and now loads
`public-pages-marketing` and `public-pages-commerce` directly, which restored
real route-level bundle separation instead of keeping one coarse public-pages
chunk
- HyperTwist also gained a repo-local `scripts/bootstrap-hypertwist-sentrux.sh`
helper plus `tools/sentrux/bin/` landing zone so the analyzer path can be
materialized under HyperTwist authority instead of depending only on
cross-repo memory
- after that same refactor/tooling continuation, the repo-local materialized
analyzer reran successfully through
`scripts/run-hypertwist-sentrux-source-only.sh` at `Quality: 6032`
- the same single residual violation remained:
`HyperTwistRecognitionTypes.h:IsStructurallyValid (308 lines)`
- that lower numeric score did not reopen cycle debt or website/page-module
god-file debt; it still reported the same one residual recognition-header hit
and no new browser/website structural regression
Latest same-lane follow-up later on `2026-06-23`:
- the remaining repeated-name recognition validator hotspot was then reduced
again by moving these larger header-local validators into
`Private/HyperTwistRecognition/HyperTwistRecognitionTypes.cpp`:
- `FHyperTwistVisionShellProfile::IsStructurallyValid()`
- `FHyperTwistVisionSolveExplanationProfile::IsStructurallyValid()`
- `FHyperTwistVisionCorrectionState::IsStructurallyValid()`
- `FHyperTwistSpeechProviderRoutingPolicy::IsStructurallyValid()`
- after that out-of-header continuation,
`scripts/run-hypertwist-sentrux-source-only.sh` reached `Quality: 6112`
and all `7` rules passed with no remaining violations
- `scripts/run-hypertwist-gitnexus-analyze.sh` then re-indexed the bounded
source-only mirror successfully at `16,055` nodes, `37,472` edges,
`645` clusters, and `300` flows
- `scripts/run-hypertwist-gitnexus-status.sh` again reported the bounded
mirror `Status: up-to-date`
- the tightened public/manual route split remained green under focused website
validation:
- `npm test -- --run src/__tests__/public-marketing-pages.test.tsx src/__tests__/protected-app-pages.test.tsx`
- `2` test files passed
- `11` tests passed
- the production website build remained green under `npm run build` in
`website/`
- the embedded browser runtime verification and production build also remained
green under:
- `npm run verify:shell` in `Content/Browser/`
- `npm run build` in `Content/Browser/`
- the same lane also preserved remote validation truth for the repaired skill
fixture packet:
- all `9` Windows Unreal automation reports under
`Skill-S3-Full-PostFixture-*` were present
- every report recorded `Succeeded: true`
- the green set covered `S3A`, `S3B`, and `S3C` continuity, authoritative,
and serialization filters
Current highest-signal structural truth after this latest continuation:
- HyperTwist now has repo-owned `sentrux` and `GitNexus` entry points that are
both revalidated and green on the bounded source-only mirror
- the public/manual/browser lane stayed validation-clean while the Unreal
validator ownership was pushed farther out of public headers
- the repaired skill-memory fixture lane is now fully green on the real remote
Windows Unreal path across all `9` focused `S3` reports
## Out of scope ## Out of scope
This note does not: This note does not:

View file

@ -50,6 +50,15 @@ Fallback connect-back shape when `22022` is occupied or stale:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p 22023 -l 'DESKTOP-KS3VGHU\\anthracite ace' localhost ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -p 22023 -l 'DESKTOP-KS3VGHU\\anthracite ace' localhost
``` ```
Verified helper shape for short Windows-side PowerShell diagnostics on the same
lane:
```bash
HYPERTWIST_REMOTE_WINDOWS_PASSWORD='...' \
scripts/run-hypertwist-remote-windows-powershell.sh \
--command 'Write-Output ("ComputerName=" + $env:COMPUTERNAME)'
```
## Current Linux-side pickup sequence ## Current Linux-side pickup sequence
For a HyperTwist AI session that starts on Linux and needs to pick up the live For a HyperTwist AI session that starts on Linux and needs to pick up the live
@ -148,6 +157,27 @@ Live follow-up on `2026-06-04` established these current facts:
- `22023` and `22024` were not visible in the latest probe and should be - `22023` and `22024` were not visible in the latest probe and should be
treated as unavailable until re-established treated as unavailable until re-established
- live `whoami` over `22022` again returned `desktop-ks3vghu\anthracite ace` - live `whoami` over `22022` again returned `desktop-ks3vghu\anthracite ace`
- the repo now also carries
`scripts/run-hypertwist-remote-windows-powershell.sh` as a bounded encoded-
PowerShell wrapper for the reverse-SSH lane so future sessions can run short
diagnostics or automation commands through `localhost:22022` without brittle
nested shell quoting
- the repo now also carries
`scripts/run-hypertwist-remote-unreal-automation.sh` as a thin first-party
helper that builds canonical `UnrealEditor-Cmd` automation invocations on top
of that encoded-PowerShell wrapper for the maintained
`C:\HyperTwist_worktrees\phase10validate` lane
- the repo now also carries
`scripts/run-hypertwist-remote-unreal-automation-sequence.sh` as a thin
first-party fail-fast helper for short ordered batches of focused automation
reruns on that same maintained validation lane
- the repo now also carries
`scripts/run-hypertwist-remote-unreal-build.sh` as a thin first-party helper
that builds canonical `Build.bat` invocations on top of that same encoded-
PowerShell wrapper for the maintained
`C:\HyperTwist_worktrees\phase10validate` lane, with the currently verified
reverse-lane low-memory posture `-NoUBA -MaxParallelActions=2` as its
default build shape
The sensitive runbook now carries the exact current Windows-side tunnel The sensitive runbook now carries the exact current Windows-side tunnel
commands, known-hosts scratch file, listener verification command, reverse-sync commands, known-hosts scratch file, listener verification command, reverse-sync
@ -564,6 +594,41 @@ Operational rules reinforced by this proof:
- when the export helper reports success, do one lightweight media sanity check - when the export helper reports success, do one lightweight media sanity check
on the canonical MP4 rather than trusting file existence alone; current proof on the canonical MP4 rather than trusting file existence alone; current proof
used `ffprobe` to confirm codec, resolution, frame count, and duration used `ffprobe` to confirm codec, resolution, frame count, and duration
- when the refreshed reverse-SSH Windows lane only has about `1.2 GB` of free
physical memory and the host is still carrying large resident
`chrome.exe` / `Code.exe` workloads, expect UnrealBuildTool to fall back to
one active compiler worker; that is a slow-but-healthy memory-pressure
condition, not by itself proof of tunnel corruption or a looping build
- when a future session needs a short bounded PowerShell diagnostic over the
reverse-SSH lane, prefer
`scripts/run-hypertwist-remote-windows-powershell.sh` over ad hoc nested
shell quoting; the wrapper was re-proved on `2026-06-23` against the live
`22022` lane with `ComputerName=DESKTOP-KS3VGHU`
- when a future session needs a focused `UnrealEditor-Cmd` automation rerun on
the maintained Windows lane, prefer
`scripts/run-hypertwist-remote-unreal-automation.sh`; its generated command
shape was dry-run-validated on `2026-06-23` for
`HyperTwist.FirstParty.Skill` before the next live post-build invocation
- when that future session instead needs a short ordered batch of those focused
automation reruns, prefer
`scripts/run-hypertwist-remote-unreal-automation-sequence.sh`; its generated
command shape was dry-run-validated on `2026-06-23`
Additional `2026-06-23` lane truth:
- the primary tunnel on `localhost:22022` remained healthy for a same-lane
skill-memory repair continuation
- the isolated Windows build root again stayed
`C:\HyperTwist_worktrees\phase10validate`
- a safe narrow-sync route was re-proved from the Windows side by using the
remote host's own `scp.exe` plus the retained VPS key to pull changed files
directly from `root@212.227.13.220` into that isolated worktree
- that route is now suitable when the active lane needs only a few touched
Unreal files refreshed in the maintained validation tree and a broad
reverse-sync would be wasteful or riskier than necessary
- after that narrow sync, the authoritative incremental Unreal editor rebuild
succeeded and the widened `S3` skill-memory automation packet finished with
all `9` expected `Skill-S3-Full-PostFixture-*` reports present and green
## Addendum - 2026-06-03 (stale-listener recovery) ## Addendum - 2026-06-03 (stale-listener recovery)

View file

@ -267,6 +267,55 @@ Operational reading:
it as stale and shift to `localhost:22023` it as stale and shift to `localhost:22023`
- if `22023` is not visibly listening, do not assume fallback availability; - if `22023` is not visibly listening, do not assume fallback availability;
re-establish it first or report that only the primary lane is currently live re-establish it first or report that only the primary lane is currently live
- when a short bounded Windows-side PowerShell diagnostic or automation command
is needed from Linux over the tunnel, prefer
`scripts/run-hypertwist-remote-windows-powershell.sh` over ad hoc nested
shell quoting; the wrapper encodes the payload for `powershell -EncodedCommand`
and was re-proved against the live `22022` lane on `2026-06-23`
- when the next step is a focused `UnrealEditor-Cmd` automation rerun on the
maintained Windows worktree, prefer
`scripts/run-hypertwist-remote-unreal-automation.sh`; it composes canonical
`-NullRHI`, `-ReportExportPath`, `-AbsLog`, and single-filter
`Automation RunTests ...; Quit` invocations on top of that encoded-
PowerShell wrapper, and its generated command shape was dry-run-validated on
`2026-06-23`
- when the next step is a short ordered batch of focused automation reruns on
that same maintained Windows worktree, prefer
`scripts/run-hypertwist-remote-unreal-automation-sequence.sh`; it composes
repeated fail-fast invocations of the single-filter wrapper in the exact
order provided, and its generated command shape was dry-run-validated on
`2026-06-23`
- when the next step is the authoritative remote Unreal editor build itself on
the maintained Windows worktree, prefer
`scripts/run-hypertwist-remote-unreal-build.sh`; it composes the verified
`Build.bat` invocation on top of that same encoded-PowerShell wrapper,
defaults the reverse-SSH lane to the maintained
`C:\HyperTwist_worktrees\phase10validate` root, and bakes in the
currently-verified low-memory recovery posture `-NoUBA -MaxParallelActions=2`
unless explicitly overridden. Its generated command shape was dry-run-
validated on `2026-06-23`
Canonical helper shape for the next focused post-build reruns:
```bash
HYPERTWIST_REMOTE_WINDOWS_PASSWORD='...' \
scripts/run-hypertwist-remote-unreal-automation.sh \
--filter HyperTwist.FirstParty.Skill \
--report-name Skill-Verify
HYPERTWIST_REMOTE_WINDOWS_PASSWORD='...' \
scripts/run-hypertwist-remote-unreal-automation.sh \
--filter HyperTwist.Permissive.WhisperCpp \
--report-name WhisperCpp-Verify
HYPERTWIST_REMOTE_WINDOWS_PASSWORD='...' \
scripts/run-hypertwist-remote-unreal-automation-sequence.sh \
--report-prefix Skill-S3 \
--filter HyperTwist.FirstParty.Skill.PhaseS3A.ContinuityResumeCoverage \
--filter HyperTwist.FirstParty.Skill.PhaseS3A.AuthoritativeStoreRead \
--filter HyperTwist.FirstParty.Skill.PhaseS3A.SerializationRoundTrip
```
- when recovering from a stale listener, run a split sequence: - when recovering from a stale listener, run a split sequence:
1. smoke login/identity check 1. smoke login/identity check
2. canonical Unreal build command with explicit result markers 2. canonical Unreal build command with explicit result markers
@ -305,11 +354,52 @@ Operational reading:
before building; preserving old mtimes can leave stale before building; preserving old mtimes can leave stale
`Intermediate\Build\Win64\UnrealEditor\Inc\UnrealHyperTwist` outputs in place `Intermediate\Build\Win64\UnrealEditor\Inc\UnrealHyperTwist` outputs in place
and surface misleading reflected-type or generated-header failures and surface misleading reflected-type or generated-header failures
- when the reverse-SSH Windows lane only has about `1.2 GB` of free physical
memory and the host still has large resident `chrome.exe` / `Code.exe`
workloads, expect UnrealBuildTool to serialize down to one active compiler
worker; treat that as a slow-but-healthy memory-pressure condition rather than
as tunnel corruption or a looping build unless the compile output itself
stalls or errors
- when a logical slice authors new `.umap` or `.uasset` authority surfaces in an - when a logical slice authors new `.umap` or `.uasset` authority surfaces in an
isolated Windows worktree, pull those assets back into the tracked repo before isolated Windows worktree, pull those assets back into the tracked repo before
calling the slice landed; remote-only authored assets are validation evidence, calling the slice landed; remote-only authored assets are validation evidence,
not final repo truth not final repo truth
## Addendum - 2026-06-23 (skill-memory fixture repair and full `S3` proof)
Live follow-up on `2026-06-23` established these additional facts:
- the maintained validation root again stayed
`C:\HyperTwist_worktrees\phase10validate`
- Windows-side `scp.exe` running on the remote host was re-proved as a safe
narrow-sync route from the VPS into that isolated worktree for touched
source files, avoiding a broad reverse-sync just to refresh a few Unreal
translation units
- after the touched skill-memory source files were hash-matched into that
worktree, the authoritative incremental editor rebuild succeeded there with
`Result: Succeeded` and UnrealBuildTool `Total execution time: 104.88
seconds`
- a fresh focused `S3A` rerun then passed on that rebuilt binary
- the widened same-lane follow-up then produced all `9` expected
`Skill-S3-Full-PostFixture-*` automation report folders under
`Saved\AutomationReports`
- each of those `9` report `index.json` files recorded a green result with
`Succeeded: true`, `SucceededCount: 1`, and `FailedCount: 0`
- that green set covered:
- `HyperTwist.FirstParty.Skill.PhaseS3A.ContinuityResumeCoverage`
- `HyperTwist.FirstParty.Skill.PhaseS3A.AuthoritativeStoreRead`
- `HyperTwist.FirstParty.Skill.PhaseS3A.SerializationRoundTrip`
- `HyperTwist.FirstParty.Skill.PhaseS3B.RecallCompactViewCoverage`
- `HyperTwist.FirstParty.Skill.PhaseS3B.AuthoritativeCompactViewLinkage`
- `HyperTwist.FirstParty.Skill.PhaseS3B.SerializationRoundTrip`
- `HyperTwist.FirstParty.Skill.PhaseS3C.WorkflowMemoryCaptureCoverage`
- `HyperTwist.FirstParty.Skill.PhaseS3C.AuthoritativeWorkflowCaptureBoundaries`
- `HyperTwist.FirstParty.Skill.PhaseS3C.SerializationRoundTrip`
This extends the doctrine from classic-cube, package, higher-dimensional, and
media-export proof into the real remote skill-memory validation lane on the
same maintained reverse-SSH path.
## Closeout wording requirement ## Closeout wording requirement
Every Unreal C++ closeout should say one of these explicitly: Every Unreal C++ closeout should say one of these explicitly:

View file

@ -71,6 +71,18 @@ Current truthful product wording should say:
- full VR/controller/settings polish lane: not yet complete enough to market as - full VR/controller/settings polish lane: not yet complete enough to market as
finished finished
## Public-surface consequence
The same truth should remain visible on the public website and public operator
manual:
- docs/resources/support surfaces should explicitly distinguish current keyboard
and higher-dimensional control ownership from unfinished VR/controller claims
- public copy should not imply that `EnhancedInput` plus motion-controller axis
groundwork already equals a finished OpenXR/runtime or rebinding lane
- browser/distribution pages may describe the simulator input posture, but they
must not market browser control parity with the native runtime
## Next clean implementation packet ## Next clean implementation packet
If the project wants to raise this lane from “groundwork exists” to “shipping If the project wants to raise this lane from “groundwork exists” to “shipping

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,82 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
dest_dir="$repo_root/tools/sentrux/bin"
dest_linux="$dest_dir/sentrux"
dest_windows="$dest_dir/sentrux.exe"
vector_sentrux_binary="/home/dev/src/VectorShell/sentrux/target/release/sentrux"
vector_sentrux_manifest="/home/dev/src/VectorShell/sentrux/Cargo.toml"
scriptorium_sentrux_windows="/home/dev/src/ScriptoriumAI/sentrux.exe"
usage() {
cat <<'EOF'
Usage:
scripts/bootstrap-hypertwist-sentrux.sh [--if-missing]
Materializes a repo-local sentrux binary under tools/sentrux/bin/ using the
best available retained source on this machine.
Resolution order:
1. existing VectorShell Linux build artifact
2. local VectorShell source build via cargo
3. local ScriptoriumAI Windows binary
EOF
}
copy_binary() {
local source_path="$1"
local destination_path="$2"
mkdir -p "$dest_dir"
cp "$source_path" "$destination_path"
chmod +x "$destination_path" 2>/dev/null || true
printf 'Materialized sentrux at %s from %s\n' "$destination_path" "$source_path"
}
if [[ "${1:-}" == "--help" ]]; then
usage
exit 0
fi
if [[ "${1:-}" == "--if-missing" ]]; then
if [[ -x "$dest_linux" || -x "$dest_windows" ]]; then
printf 'Repo-local sentrux already present under %s\n' "$dest_dir"
exit 0
fi
elif [[ $# -gt 0 ]]; then
usage >&2
exit 1
fi
if [[ -x "$vector_sentrux_binary" ]]; then
copy_binary "$vector_sentrux_binary" "$dest_linux"
exit 0
fi
if command -v cargo >/dev/null 2>&1 && [[ -f "$vector_sentrux_manifest" ]]; then
cargo build --quiet --release --manifest-path "$vector_sentrux_manifest" --bin sentrux
if [[ -x "$vector_sentrux_binary" ]]; then
copy_binary "$vector_sentrux_binary" "$dest_linux"
exit 0
fi
fi
if [[ -f "$scriptorium_sentrux_windows" ]]; then
copy_binary "$scriptorium_sentrux_windows" "$dest_windows"
exit 0
fi
cat >&2 <<EOF
Unable to materialize a repo-local sentrux binary.
Checked:
- $vector_sentrux_binary
- $vector_sentrux_manifest
- $scriptorium_sentrux_windows
Provide HYPERTWIST_SENTRUX_BINARY directly or place a compatible sentrux binary
under tools/sentrux/bin/.
EOF
exit 1

View file

@ -0,0 +1,102 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
scripts/run-hypertwist-remote-unreal-automation-sequence.sh \
--filter HyperTwist.FirstParty.Skill.PhaseS3A.ContinuityResumeCoverage \
--filter HyperTwist.FirstParty.Skill.PhaseS3A.AuthoritativeStoreRead
Options:
--filter <automation-filter>
Required. May be repeated. Filters are run sequentially in the order given.
--report-prefix <name>
Optional prefix for per-filter report names.
--extra-arg <value>
Append an extra UnrealEditor-Cmd.exe argument to every filter run.
--dry-run
Print the underlying wrapper invocations instead of executing them.
EOF
}
filters=()
report_prefix=""
dry_run="false"
extra_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
--filter)
filters+=("${2:-}")
shift 2
;;
--report-prefix)
report_prefix="${2:-}"
shift 2
;;
--extra-arg)
extra_args+=("${2:-}")
shift 2
;;
--dry-run)
dry_run="true"
shift
;;
-h|--help)
usage
exit 0
;;
*)
usage >&2
exit 1
;;
esac
done
if [[ ${#filters[@]} -eq 0 ]]; then
echo "Provide at least one --filter." >&2
exit 1
fi
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
single_wrapper="${script_dir}/run-hypertwist-remote-unreal-automation.sh"
if [[ ! -x "$single_wrapper" ]]; then
echo "Expected executable wrapper at $single_wrapper" >&2
exit 1
fi
sanitize_report_name() {
printf '%s' "$1" | sed 's/[^[:alnum:]._+-]/_/g'
}
for filter in "${filters[@]}"; do
report_name="$(sanitize_report_name "$filter")"
if [[ -n "$report_prefix" ]]; then
report_name="${report_prefix}-$(sanitize_report_name "$filter")"
fi
command=(
"$single_wrapper"
--filter "$filter"
--report-name "$report_name"
)
for extra_arg in "${extra_args[@]}"; do
command+=(--extra-arg "$extra_arg")
done
if [[ "$dry_run" == "true" ]]; then
printf '%q ' "${command[@]}"
printf '\n'
continue
fi
echo "Running remote Unreal automation filter: $filter"
"${command[@]}"
done

View file

@ -0,0 +1,161 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
scripts/run-hypertwist-remote-unreal-automation.sh --filter HyperTwist.FirstParty.Skill
Options:
--filter <automation-filter>
Required Unreal automation filter.
--report-name <name>
Optional report/log folder suffix. Defaults to a sanitized filter name.
--extra-arg <value>
Append an extra UnrealEditor-Cmd.exe argument. May be repeated.
--dry-run
Print the generated remote PowerShell payload instead of executing it.
Environment:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD Required by the underlying tunnel wrapper.
HYPERTWIST_REMOTE_WINDOWS_WORKTREE_ROOT Optional, defaults to C:\HyperTwist_worktrees\phase10validate
HYPERTWIST_REMOTE_WINDOWS_PROJECT_PATH Optional, defaults beneath the worktree root.
HYPERTWIST_REMOTE_WINDOWS_UNREAL_EDITOR_CMD
Optional, defaults to the UE 5.7 UnrealEditor-Cmd.exe path.
HYPERTWIST_REMOTE_WINDOWS_AUTOMATION_REPORT_ROOT
Optional, defaults beneath Saved\AutomationReports.
HYPERTWIST_REMOTE_WINDOWS_LOG_ROOT Optional, defaults beneath Saved\Logs.
EOF
}
filter=""
report_name=""
dry_run="false"
extra_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
--filter)
filter="${2:-}"
shift 2
;;
--report-name)
report_name="${2:-}"
shift 2
;;
--extra-arg)
extra_args+=("${2:-}")
shift 2
;;
--dry-run)
dry_run="true"
shift
;;
-h|--help)
usage
exit 0
;;
*)
usage >&2
exit 1
;;
esac
done
if [[ -z "$filter" ]]; then
echo "Provide --filter." >&2
exit 1
fi
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
remote_ps_wrapper="${script_dir}/run-hypertwist-remote-windows-powershell.sh"
if [[ ! -x "$remote_ps_wrapper" ]]; then
echo "Expected executable wrapper at $remote_ps_wrapper" >&2
exit 1
fi
worktree_root="${HYPERTWIST_REMOTE_WINDOWS_WORKTREE_ROOT:-C:\\HyperTwist_worktrees\\phase10validate}"
project_path="${HYPERTWIST_REMOTE_WINDOWS_PROJECT_PATH:-${worktree_root}\\UnrealHyperTwist\\UnrealHyperTwist.uproject}"
editor_cmd_path="${HYPERTWIST_REMOTE_WINDOWS_UNREAL_EDITOR_CMD:-C:\\Program Files\\Epic Games\\UE_5.7\\Engine\\Binaries\\Win64\\UnrealEditor-Cmd.exe}"
report_root="${HYPERTWIST_REMOTE_WINDOWS_AUTOMATION_REPORT_ROOT:-${worktree_root}\\UnrealHyperTwist\\Saved\\AutomationReports}"
log_root="${HYPERTWIST_REMOTE_WINDOWS_LOG_ROOT:-${worktree_root}\\UnrealHyperTwist\\Saved\\Logs}"
if [[ -z "$report_name" ]]; then
report_name="$(printf '%s' "$filter" | sed 's/[^[:alnum:]._+-]/_/g')"
fi
report_export_path="${report_root}\\${report_name}"
abs_log_path="${log_root}\\${report_name}.log"
powershell_payload="$(
python3 - <<'PY' \
"$editor_cmd_path" \
"$project_path" \
"$report_export_path" \
"$abs_log_path" \
"$filter" \
"${extra_args[@]}"
import sys
editor_cmd_path, project_path, report_export_path, abs_log_path, filter_name, *extra_args = sys.argv[1:]
def ps_single_quote(value: str) -> str:
return "'" + value.replace("'", "''") + "'"
lines = [
f"$EditorCmdPath = {ps_single_quote(editor_cmd_path)}",
f"$ProjectPath = {ps_single_quote(project_path)}",
f"$ReportExportPath = {ps_single_quote(report_export_path)}",
f"$AbsLogPath = {ps_single_quote(abs_log_path)}",
f"$Filter = {ps_single_quote(filter_name)}",
"",
"New-Item -ItemType Directory -Force -Path $ReportExportPath | Out-Null",
"New-Item -ItemType Directory -Force -Path (Split-Path -Parent $AbsLogPath) | Out-Null",
"",
"if (-not (Test-Path -LiteralPath $EditorCmdPath)) {",
" throw \"UnrealEditor-Cmd.exe was not found at '$EditorCmdPath'.\"",
"}",
"",
"if (-not (Test-Path -LiteralPath $ProjectPath)) {",
" throw \"Project file was not found at '$ProjectPath'.\"",
"}",
"",
"& $EditorCmdPath `",
" $ProjectPath `",
" -unattended `",
" -nop4 `",
" -nosplash `",
" -NullRHI `",
" -log `",
" -stdout `",
" -FullStdOutLogOutput `",
" \"-AbsLog=$AbsLogPath\" `",
" \"-ReportExportPath=$ReportExportPath\" `",
" \"-ExecCmds=Automation RunTests $Filter\" `",
]
if extra_args:
for extra_arg in extra_args:
lines.append(f" {ps_single_quote(extra_arg)} `")
lines.extend([
" \"-TestExit=Automation Test Queue Empty\"",
"",
"exit $LASTEXITCODE",
])
print("\n".join(lines))
PY
)"
if [[ "$dry_run" == "true" ]]; then
printf '%s\n' "$powershell_payload"
exit 0
fi
exec "$remote_ps_wrapper" --command "$powershell_payload"

View file

@ -0,0 +1,213 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
scripts/run-hypertwist-remote-unreal-build.sh
Options:
--target <name>
Optional Unreal build target. Defaults to UnrealHyperTwistEditor.
--platform <name>
Optional Unreal platform. Defaults to Win64.
--configuration <name>
Optional Unreal configuration. Defaults to Development.
--worktree-root <path>
Optional Windows worktree root. Defaults to C:\HyperTwist_worktrees\phase10validate.
--project-path <path>
Optional Windows .uproject path. Defaults beneath the worktree root.
--build-batch-path <path>
Optional Build.bat path. Defaults to the UE 5.7 installed-engine path.
--max-parallel-actions <count>
Optional MaxParallelActions override. Defaults to 2 for the remote lane.
--allow-uba
Allow UBA instead of forcing the verified remote-lane -NoUBA posture.
--extra-arg <value>
Append an extra Build.bat argument. May be repeated.
--dry-run
Print the generated remote PowerShell payload instead of executing it.
Environment:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD Required by the underlying tunnel wrapper.
HYPERTWIST_REMOTE_WINDOWS_WORKTREE_ROOT Optional default for --worktree-root.
HYPERTWIST_REMOTE_WINDOWS_PROJECT_PATH Optional default for --project-path.
HYPERTWIST_REMOTE_WINDOWS_BUILD_BAT Optional default for --build-batch-path.
HYPERTWIST_REMOTE_WINDOWS_MAX_PARALLEL_ACTIONS
Optional default for --max-parallel-actions.
EOF
}
target_name="UnrealHyperTwistEditor"
platform_name="Win64"
configuration_name="Development"
worktree_root="${HYPERTWIST_REMOTE_WINDOWS_WORKTREE_ROOT:-C:\\HyperTwist_worktrees\\phase10validate}"
project_path=""
build_batch_path="${HYPERTWIST_REMOTE_WINDOWS_BUILD_BAT:-C:\\Program Files\\Epic Games\\UE_5.7\\Engine\\Build\\BatchFiles\\Build.bat}"
max_parallel_actions="${HYPERTWIST_REMOTE_WINDOWS_MAX_PARALLEL_ACTIONS:-2}"
allow_uba="false"
dry_run="false"
extra_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
--target)
target_name="${2:-}"
shift 2
;;
--platform)
platform_name="${2:-}"
shift 2
;;
--configuration)
configuration_name="${2:-}"
shift 2
;;
--worktree-root)
worktree_root="${2:-}"
shift 2
;;
--project-path)
project_path="${2:-}"
shift 2
;;
--build-batch-path)
build_batch_path="${2:-}"
shift 2
;;
--max-parallel-actions)
max_parallel_actions="${2:-}"
shift 2
;;
--allow-uba)
allow_uba="true"
shift
;;
--extra-arg)
extra_args+=("${2:-}")
shift 2
;;
--dry-run)
dry_run="true"
shift
;;
-h|--help)
usage
exit 0
;;
*)
usage >&2
exit 1
;;
esac
done
if [[ -z "$project_path" ]]; then
project_path="${HYPERTWIST_REMOTE_WINDOWS_PROJECT_PATH:-${worktree_root}\\UnrealHyperTwist\\UnrealHyperTwist.uproject}"
fi
if [[ -z "$target_name" || -z "$platform_name" || -z "$configuration_name" || -z "$project_path" || -z "$build_batch_path" ]]; then
echo "Target, platform, configuration, project path, and Build.bat path must all be populated." >&2
exit 1
fi
if [[ -n "$max_parallel_actions" && ! "$max_parallel_actions" =~ ^[0-9]+$ ]]; then
echo "--max-parallel-actions must be numeric." >&2
exit 1
fi
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
remote_ps_wrapper="${script_dir}/run-hypertwist-remote-windows-powershell.sh"
if [[ ! -x "$remote_ps_wrapper" ]]; then
echo "Expected executable wrapper at $remote_ps_wrapper" >&2
exit 1
fi
powershell_payload="$(
python3 - <<'PY' \
"$build_batch_path" \
"$target_name" \
"$platform_name" \
"$configuration_name" \
"$project_path" \
"$max_parallel_actions" \
"$allow_uba" \
"${extra_args[@]}"
import sys
build_batch_path, target_name, platform_name, configuration_name, project_path, max_parallel_actions, allow_uba, *extra_args = sys.argv[1:]
def ps_single_quote(value: str) -> str:
return "'" + value.replace("'", "''") + "'"
lines = [
f"$BuildBatchPath = {ps_single_quote(build_batch_path)}",
f"$TargetName = {ps_single_quote(target_name)}",
f"$PlatformName = {ps_single_quote(platform_name)}",
f"$ConfigurationName = {ps_single_quote(configuration_name)}",
f"$ProjectPath = {ps_single_quote(project_path)}",
f"$MaxParallelActions = {max_parallel_actions}",
f"$AllowUba = ${'true' if allow_uba == 'true' else 'false'}",
"$ExtraArgs = @(",
]
for value in extra_args:
lines.append(f" {ps_single_quote(value)}")
lines.extend([
")",
"",
"if (-not (Test-Path -LiteralPath $BuildBatchPath)) {",
" throw \"Build.bat was not found at '$BuildBatchPath'.\"",
"}",
"",
"if (-not (Test-Path -LiteralPath $ProjectPath)) {",
" throw \"Project file was not found at '$ProjectPath'.\"",
"}",
"",
"$BuildArgs = @(",
" $TargetName",
" $PlatformName",
" $ConfigurationName",
" $ProjectPath",
" '-WaitMutex'",
" '-NoHotReloadFromIDE'",
")",
"",
"if (-not $AllowUba) {",
" $BuildArgs += '-NoUBA'",
"}",
"",
"if ($MaxParallelActions -gt 0) {",
" $BuildArgs += \"-MaxParallelActions=$MaxParallelActions\"",
"}",
"",
"if ($ExtraArgs.Count -gt 0) {",
" $BuildArgs += $ExtraArgs",
"}",
"",
"& $BuildBatchPath @BuildArgs",
"exit $LASTEXITCODE",
])
print("\n".join(lines))
PY
)"
if [[ "$dry_run" == "true" ]]; then
printf '%s\n' "$powershell_payload"
exit 0
fi
exec "$remote_ps_wrapper" --command "$powershell_payload"

View file

@ -0,0 +1,142 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
scripts/run-hypertwist-remote-windows-file-sync.sh \
--file UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp
Options:
--file <path>
Relative repo path to copy into the remote Windows worktree. May be repeated.
--remote-root <path>
Optional Windows worktree root. Defaults to C:\HyperTwist_worktrees\phase10validate.
--dry-run
Print the copy plan without transferring files.
Environment:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD Required password for the reverse-SSH Windows user.
HYPERTWIST_REMOTE_TUNNEL_PORT Optional, defaults to 22022.
HYPERTWIST_REMOTE_WINDOWS_USER Optional, defaults to "anthracite ace".
HYPERTWIST_REMOTE_TUNNEL_HOST Optional, defaults to "localhost".
EOF
}
if ! command -v sshpass >/dev/null 2>&1; then
echo "sshpass is required but was not found on PATH." >&2
exit 1
fi
if ! command -v sha256sum >/dev/null 2>&1; then
echo "sha256sum is required but was not found on PATH." >&2
exit 1
fi
if [[ -z "${HYPERTWIST_REMOTE_WINDOWS_PASSWORD:-}" ]]; then
echo "Set HYPERTWIST_REMOTE_WINDOWS_PASSWORD before using this wrapper." >&2
exit 1
fi
remote_root='C:\HyperTwist_worktrees\phase10validate'
chunk_size_bytes=65536
dry_run="false"
files=()
while [[ $# -gt 0 ]]; do
case "$1" in
--file)
files+=("${2:-}")
shift 2
;;
--remote-root)
remote_root="${2:-}"
shift 2
;;
--dry-run)
dry_run="true"
shift
;;
-h|--help)
usage
exit 0
;;
*)
usage >&2
exit 1
;;
esac
done
if [[ ${#files[@]} -eq 0 ]]; then
echo "Provide at least one --file." >&2
exit 1
fi
remote_port="${HYPERTWIST_REMOTE_TUNNEL_PORT:-22022}"
remote_user="${HYPERTWIST_REMOTE_WINDOWS_USER:-anthracite ace}"
remote_host="${HYPERTWIST_REMOTE_TUNNEL_HOST:-localhost}"
for relative_path in "${files[@]}"; do
if [[ "$relative_path" = /* ]]; then
echo "Use repo-relative paths only: $relative_path" >&2
exit 1
fi
if [[ ! -f "$relative_path" ]]; then
echo "File not found: $relative_path" >&2
exit 1
fi
windows_relative_path="${relative_path//\//\\}"
windows_dest_path="${remote_root}\\${windows_relative_path}"
local_hash="$(sha256sum "$relative_path" | awk '{print tolower($1)}')"
if [[ "$dry_run" == "true" ]]; then
printf '%s -> %s (%s)\n' "$relative_path" "$windows_dest_path" "$local_hash"
continue
fi
ssh_base=(
sshpass -p "${HYPERTWIST_REMOTE_WINDOWS_PASSWORD}"
ssh
-o StrictHostKeyChecking=no
-o PreferredAuthentications=password
-o PubkeyAuthentication=no
-p "${remote_port}"
-l "${remote_user}"
"${remote_host}"
)
"${ssh_base[@]}" \
"powershell -NoProfile -Command \"\$dest='${windows_dest_path}'; \$destDir = Split-Path -Parent \$dest; New-Item -ItemType Directory -Force -Path \$destDir | Out-Null; \$outputStream = [IO.File]::Open(\$dest, [IO.FileMode]::Create, [IO.FileAccess]::Write, [IO.FileShare]::None); \$outputStream.Dispose()\""
tmp_chunk_dir="$(mktemp -d)"
split -b "${chunk_size_bytes}" --numeric-suffixes=1 --suffix-length=4 \
"$relative_path" "${tmp_chunk_dir}/chunk-"
for chunk_path in "${tmp_chunk_dir}"/chunk-*; do
cat "$chunk_path" | \
"${ssh_base[@]}" \
"powershell -NoProfile -Command \"\$dest='${windows_dest_path}'; \$outputStream = [IO.File]::Open(\$dest, [IO.FileMode]::Append, [IO.FileAccess]::Write, [IO.FileShare]::None); try { [Console]::OpenStandardInput().CopyTo(\$outputStream) } finally { \$outputStream.Dispose() }\""
done
remote_hash="$(
"${ssh_base[@]}" \
"powershell -NoProfile -Command \"[Console]::Out.Write((Get-FileHash -LiteralPath '${windows_dest_path}' -Algorithm SHA256).Hash.ToLowerInvariant())\""
)"
rm -rf "$tmp_chunk_dir"
if [[ "$remote_hash" != "$local_hash" ]]; then
echo "Hash mismatch for $relative_path" >&2
echo " local : $local_hash" >&2
echo " remote: $remote_hash" >&2
exit 1
fi
printf 'Synced %s -> %s (%s)\n' "$relative_path" "$windows_dest_path" "$local_hash"
done

View file

@ -0,0 +1,90 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
scripts/run-hypertwist-remote-windows-powershell.sh --command "Write-Output 'hello'"
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
scripts/run-hypertwist-remote-windows-powershell.sh --script-file ./script.ps1
Options:
--command <powershell>
Run the provided PowerShell source text.
--script-file <path>
Read PowerShell source from the given file.
Environment:
HYPERTWIST_REMOTE_WINDOWS_PASSWORD Required password for the reverse-SSH Windows user.
HYPERTWIST_REMOTE_TUNNEL_PORT Optional, defaults to 22022.
HYPERTWIST_REMOTE_WINDOWS_USER Optional, defaults to "anthracite ace".
HYPERTWIST_REMOTE_TUNNEL_HOST Optional, defaults to "localhost".
EOF
}
if [[ $# -lt 2 ]]; then
usage >&2
exit 1
fi
if ! command -v sshpass >/dev/null 2>&1; then
echo "sshpass is required but was not found on PATH." >&2
exit 1
fi
if [[ -z "${HYPERTWIST_REMOTE_WINDOWS_PASSWORD:-}" ]]; then
echo "Set HYPERTWIST_REMOTE_WINDOWS_PASSWORD before using this wrapper." >&2
exit 1
fi
script_text=""
case "$1" in
--command)
script_text="$2"
;;
--script-file)
if [[ ! -f "$2" ]]; then
echo "Script file not found: $2" >&2
exit 1
fi
script_text="$(<"$2")"
;;
-h|--help)
usage
exit 0
;;
*)
usage >&2
exit 1
;;
esac
remote_port="${HYPERTWIST_REMOTE_TUNNEL_PORT:-22022}"
remote_user="${HYPERTWIST_REMOTE_WINDOWS_USER:-anthracite ace}"
remote_host="${HYPERTWIST_REMOTE_TUNNEL_HOST:-localhost}"
script_preamble=$'$ProgressPreference = \'SilentlyContinue\'\n$ErrorActionPreference = \'Stop\'\n'
script_payload="${script_preamble}${script_text}"
encoded_command="$(
python3 - <<'PY' "$script_payload"
import base64
import sys
script = sys.argv[1]
print(base64.b64encode(script.encode("utf-16le")).decode("ascii"))
PY
)"
exec sshpass -p "${HYPERTWIST_REMOTE_WINDOWS_PASSWORD}" \
ssh \
-o StrictHostKeyChecking=no \
-o PreferredAuthentications=password \
-o PubkeyAuthentication=no \
-p "${remote_port}" \
-l "${remote_user}" \
"${remote_host}" \
"powershell -NoProfile -EncodedCommand ${encoded_command}"

View file

@ -5,6 +5,8 @@ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
temp_root="${TMPDIR:-/tmp}/hypertwist-sentrux-source-only" temp_root="${TMPDIR:-/tmp}/hypertwist-sentrux-source-only"
repo_local_sentrux_binary="$repo_root/sentrux" repo_local_sentrux_binary="$repo_root/sentrux"
repo_local_sentrux_windows_binary="$repo_root/sentrux.exe" repo_local_sentrux_windows_binary="$repo_root/sentrux.exe"
repo_tools_sentrux_binary="$repo_root/tools/sentrux/bin/sentrux"
repo_tools_sentrux_windows_binary="$repo_root/tools/sentrux/bin/sentrux.exe"
local_sentrux_binary="/home/dev/src/VectorShell/sentrux/target/release/sentrux" local_sentrux_binary="/home/dev/src/VectorShell/sentrux/target/release/sentrux"
local_sentrux_manifest="/home/dev/src/VectorShell/sentrux/Cargo.toml" local_sentrux_manifest="/home/dev/src/VectorShell/sentrux/Cargo.toml"
@ -24,6 +26,16 @@ resolve_sentrux_command() {
return 0 return 0
fi fi
if [[ -x "$repo_tools_sentrux_binary" ]]; then
printf '%s\n' "$repo_tools_sentrux_binary"
return 0
fi
if [[ -x "$repo_tools_sentrux_windows_binary" ]]; then
printf '%s\n' "$repo_tools_sentrux_windows_binary"
return 0
fi
if command -v sentrux >/dev/null 2>&1; then if command -v sentrux >/dev/null 2>&1; then
printf 'sentrux\n' printf 'sentrux\n'
return 0 return 0
@ -43,7 +55,7 @@ resolve_sentrux_command() {
} }
sentrux_command="$(resolve_sentrux_command)" || { sentrux_command="$(resolve_sentrux_command)" || {
echo "Unable to locate sentrux. Provide HYPERTWIST_SENTRUX_BINARY, add sentrux to PATH, place a repo-local sentrux binary at $repo_root, or keep /home/dev/src/VectorShell/sentrux available." >&2 echo "Unable to locate sentrux. Provide HYPERTWIST_SENTRUX_BINARY, run scripts/bootstrap-hypertwist-sentrux.sh, add sentrux to PATH, place a repo-local sentrux binary at $repo_root, or keep /home/dev/src/VectorShell/sentrux available." >&2
exit 1 exit 1
} }

15
tools/sentrux/README.md Normal file
View file

@ -0,0 +1,15 @@
# HyperTwist-local sentrux landing zone
This directory is the HyperTwist-owned location for a repo-local `sentrux`
binary.
Do not commit platform binaries here.
Use:
```bash
scripts/bootstrap-hypertwist-sentrux.sh
```
That helper materializes a local binary under `tools/sentrux/bin/` from the
best retained source available on this machine.

View file

@ -0,0 +1 @@

View file

@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render, screen } from '@testing-library/react' import { cleanup, render, screen } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { MemoryRouter } from 'react-router-dom' import { MemoryRouter } from 'react-router-dom'
import { ROUTER_FUTURE_FLAGS } from '../router/router-future'
const mockUsePlatformAuth = vi.fn() const mockUsePlatformAuth = vi.fn()
const mockGetAuthHealth = vi.fn() const mockGetAuthHealth = vi.fn()
@ -31,7 +32,7 @@ function renderPage(page: React.ReactNode, initialEntry: string) {
return render( return render(
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[initialEntry]}> <MemoryRouter initialEntries={[initialEntry]} future={ROUTER_FUTURE_FLAGS}>
{page} {page}
</MemoryRouter> </MemoryRouter>
</QueryClientProvider>, </QueryClientProvider>,

View file

@ -547,6 +547,8 @@ describe('public marketing pages', () => {
expect(screen.getByText('Selected help lane')).toBeTruthy() expect(screen.getByText('Selected help lane')).toBeTruthy()
expect(screen.getByText('Launch readiness')).toBeTruthy() expect(screen.getByText('Launch readiness')).toBeTruthy()
expect(screen.getByText(/turning the preview lane into a public launch/i)).toBeTruthy() expect(screen.getByText(/turning the preview lane into a public launch/i)).toBeTruthy()
expect(screen.getByText('If the desktop app is primary, why keep the web version?')).toBeTruthy()
expect(screen.getByText('Is VR/controller support already fully finished?')).toBeTruthy()
expect(screen.getByText('Support lanes')).toBeTruthy() expect(screen.getByText('Support lanes')).toBeTruthy()
}) })
@ -650,6 +652,10 @@ describe('public marketing pages', () => {
expect(screen.getAllByText(/MagicCube5D dedicated-family training map: passed/i).length).toBeGreaterThan(0) expect(screen.getAllByText(/MagicCube5D dedicated-family training map: passed/i).length).toBeGreaterThan(0)
expect(screen.getByText('Operator playbooks')).toBeTruthy() expect(screen.getByText('Operator playbooks')).toBeTruthy()
expect(screen.getByText('Simulator use today')).toBeTruthy() expect(screen.getByText('Simulator use today')).toBeTruthy()
expect(screen.getByText('Higher-dimensional runtime guide')).toBeTruthy()
expect(screen.getByText('Current control and device posture')).toBeTruthy()
expect(screen.getByText('Deployment readiness snapshot')).toBeTruthy()
expect(screen.getByText('XR groundwork exists, but the full VR lane is not finished')).toBeTruthy()
expect(screen.getByText('Higher-dimensional runtime ownership')).toBeTruthy() expect(screen.getByText('Higher-dimensional runtime ownership')).toBeTruthy()
}) })
@ -715,6 +721,10 @@ describe('public marketing pages', () => {
expect(screen.getByText('Operator manual')).toBeTruthy() expect(screen.getByText('Operator manual')).toBeTruthy()
expect(screen.getByText('1. Start in the browser shell')).toBeTruthy() expect(screen.getByText('1. Start in the browser shell')).toBeTruthy()
expect(screen.getByText('Simulator manual')).toBeTruthy() expect(screen.getByText('Simulator manual')).toBeTruthy()
expect(screen.getByText('Higher-dimensional family guide')).toBeTruthy()
expect(screen.getByText('Deployment readiness manual')).toBeTruthy()
expect(screen.getByText('Input and device posture')).toBeTruthy()
expect(screen.getAllByText('Keyboard and mouse ship today').length).toBeGreaterThan(0)
expect(screen.getByText('Feature-registry-backed wording only')).toBeTruthy() expect(screen.getByText('Feature-registry-backed wording only')).toBeTruthy()
expect(await screen.findByRole('link', { name: /open public docs portal/i })).toBeTruthy() expect(await screen.findByRole('link', { name: /open public docs portal/i })).toBeTruthy()
}) })

View file

@ -0,0 +1,88 @@
import type { ReactNode } from 'react'
import { Link } from 'react-router-dom'
import {
brandConfig,
downloadTargets,
mplSourceUrl,
openSourceRepoUrl,
planCatalog,
publicDocsUrl,
releaseNotesUrl,
} from '../site-config'
import { isExternalHref } from '../site-routes'
export const supportTopicGuidance: Record<string, { title: string; description: string }> = {
'launch-readiness': {
title: 'Launch readiness',
description:
'Need help turning the preview lane into a public launch? We can walk through checkout wiring, download release targets, notices, and corresponding-source publication.',
},
'operator-access': {
title: 'Operator access',
description:
'Use this lane when you need operator checkout, entitlement enablement, or help reaching the protected desktop-download surface.',
},
'studio-rollout': {
title: 'Studio rollout',
description:
'Use this lane for higher-dimensional rollout planning, deployment coordination, or production-lane package and notice readiness.',
},
}
export const explorerFallbackPlan = planCatalog.find((plan) => plan.key === 'explorer') ?? planCatalog[0]
export const operatorFallbackPlan = planCatalog.find((plan) => plan.key === 'operator') ?? planCatalog[1]
export const studioFallbackPlan = planCatalog.find((plan) => plan.key === 'studio') ?? planCatalog[2]
export const releaseCommerceFallback = {
operatorCheckoutUrl: isExternalHref(operatorFallbackPlan.ctaHref) ? operatorFallbackPlan.ctaHref : '',
studioCheckoutUrl: isExternalHref(studioFallbackPlan.ctaHref) ? studioFallbackPlan.ctaHref : '',
planPriceOperator: operatorFallbackPlan.price,
planPriceStudio: studioFallbackPlan.price,
}
export function buildPublicReleaseManifestFallback(supportEmail = brandConfig.contact.email) {
return {
downloadTargets,
publicDocsUrl,
releaseNotesUrl,
correspondingSourceUrl: mplSourceUrl,
openSourceRepoUrl,
supportEmail,
}
}
export function Section({
title,
description,
children,
}: {
title: string
description?: string
children: ReactNode
}) {
return (
<section className="page-section">
<div className="section-heading">
<h2>{title}</h2>
{description ? <p>{description}</p> : null}
</div>
{children}
</section>
)
}
export function PlanActionLink({ href, label }: { href: string; label: string }) {
if (isExternalHref(href)) {
return (
<a className="button button--primary button--full" href={href} target="_blank" rel="noreferrer">
{label}
</a>
)
}
return (
<Link className="button button--primary button--full" to={href}>
{label}
</Link>
)
}

View file

@ -0,0 +1,536 @@
import { useMemo } from 'react'
import { useQuery } from '@tanstack/react-query'
import { Link } from 'react-router-dom'
import { getReleaseManifest } from '../auth/auth-api'
import { MarketingShell } from '../components/layout/MarketingShell'
import { SiteMetadata } from '../components/seo/SiteMetadata'
import { PublicLaunchStatus } from '../components/ui/PublicLaunchStatus'
import { ReleaseValidationSummary } from '../components/ui/ReleaseValidationSummary'
import { paddleReadyDescription } from '../site-config'
import {
buildReleaseMetadataItems,
resolveReleaseCommerceView,
resolveReleaseManifestView,
} from '../release-manifest'
import { buildProtectedDownloadPath, buildSupportPath } from '../site-routes'
import {
deliverySurfaceCards,
desktopDownloadSteps,
desktopReleaseSignals,
digitalDeliveryCards,
distributionDoctrineCards,
openSourceNotices,
privacyBoundaryCards,
termsBoundaryCards,
} from '../site-data'
import {
buildPublicReleaseManifestFallback,
explorerFallbackPlan,
operatorFallbackPlan,
PlanActionLink,
releaseCommerceFallback,
Section,
studioFallbackPlan,
} from './public-page-helpers'
export function PricingPage() {
const releaseManifestQuery = useQuery({
queryKey: ['release-manifest', 'public'],
queryFn: getReleaseManifest,
retry: false,
})
const releaseCommerce = useMemo(
() => resolveReleaseCommerceView(releaseManifestQuery.data?.manifest, releaseCommerceFallback),
[releaseManifestQuery.data?.manifest],
)
const runtimePlanCatalog = useMemo(() => ([
explorerFallbackPlan,
{
...operatorFallbackPlan,
price: releaseCommerce.plan_price_operator,
ctaLabel: releaseCommerce.operator_checkout_url ? 'Open Paddle checkout' : 'Request operator access',
ctaHref: releaseCommerce.operator_checkout_url || buildSupportPath('operator-access'),
},
{
...studioFallbackPlan,
price: releaseCommerce.plan_price_studio,
ctaLabel: releaseCommerce.studio_checkout_url ? 'Open Paddle checkout' : 'Talk to HyperTwist',
ctaHref: releaseCommerce.studio_checkout_url || buildSupportPath('studio-rollout'),
},
]), [releaseCommerce])
return (
<>
<SiteMetadata
title="HyperTwist pricing"
description="View HyperTwist browser-access, operator, and studio pricing with Paddle-ready checkout posture and launch-honest download/legal guidance."
canonicalPath="/pricing"
/>
<MarketingShell
eyebrow="Paddle-ready plans"
title="Pricing that matches the actual delivery model."
lede="The browser shell handles account, release, and billing access. The simulator remains desktop-first. Prices and checkouts can be switched live through the same Paddle-ready structure used across the broader product family."
>
<Section title="Plan lineup" description={paddleReadyDescription}>
<div className="card-grid card-grid--pricing">
{runtimePlanCatalog.map((plan) => (
<article key={plan.key} className="card card--pricing">
<p className="status-pill">{plan.name}</p>
<h3>{plan.price}</h3>
<p>{plan.notes}</p>
<ul className="list">
{plan.features.map((feature) => (
<li key={feature}>{feature}</li>
))}
</ul>
<PlanActionLink href={plan.ctaHref} label={plan.ctaLabel} />
</article>
))}
</div>
</Section>
<Section title="Launch posture">
<PublicLaunchStatus />
</Section>
<Section
title="Why plans live in the browser while training stays native"
description="Commercial access, entitlement, and launch-readiness posture belong to the browser shell so the simulator can stay focused on training quality."
>
<div className="card-grid">
{deliverySurfaceCards.slice(0, 3).map((surface) => (
<article key={surface.title} className="card">
<h3>{surface.title}</h3>
<p>{surface.description}</p>
</article>
))}
</div>
</Section>
<Section title="Important launch note">
<article className="callout">
<p>
Public pricing, checkout, and download pages are distribution surfaces. Before external launch,
keep their legal footer and open-source notices link live and ensure the corresponding-source URL is configured for any downloadable build containing MPL-covered material.
</p>
</article>
</Section>
</MarketingShell>
</>
)
}
export function DownloadPage() {
const releaseManifestQuery = useQuery({
queryKey: ['release-manifest', 'public'],
queryFn: getReleaseManifest,
retry: false,
})
const releaseManifest = useMemo(
() => resolveReleaseManifestView(releaseManifestQuery.data?.manifest, buildPublicReleaseManifestFallback()),
[releaseManifestQuery.data?.manifest],
)
return (
<>
<SiteMetadata
title="Download HyperTwist desktop"
description="Download the HyperTwist desktop build, preserve your target platform into the protected release surface, and pair the installed app with your browser account."
canonicalPath="/download"
/>
<MarketingShell
eyebrow="Desktop distribution"
title="Download the desktop build and pair it with your browser account."
lede="The website provides account, release, and legal surfaces. The actual simulator ships through the desktop lane, with package validation and release discipline carried over from the HyperTwist roadmap."
>
{releaseManifestQuery.isLoading ? (
<Section title="Release manifest status">
<article className="callout">
<p>Loading the current server-backed release manifest. Static preview metadata remains visible until the live manifest arrives.</p>
</article>
</Section>
) : null}
{releaseManifestQuery.isError ? (
<Section title="Release manifest status">
<article className="callout">
<p>
The live release manifest could not be loaded from the auth server right now.
This page is showing bounded fallback site metadata instead of current runtime release authority.
</p>
</article>
</Section>
) : null}
<Section title="Available targets">
<div className="card-grid">
{releaseManifest.platforms.map((platform) => {
const metadataItems = buildReleaseMetadataItems(platform)
return (
<article key={platform.platform_key} className="card">
<h3>{platform.platform}</h3>
<p className="status-pill">{platform.subtitle}</p>
<p>{platform.details}</p>
{metadataItems.length > 0 ? (
<ul className="list top-gap">
{metadataItems.map((item) => (
<li key={`${platform.platform_key}-${item.label}`}>
{item.label}: {item.value}
</li>
))}
</ul>
) : null}
<ReleaseValidationSummary platform={platform} />
{platform.configured ? (
<Link className="button button--primary button--full" to={buildProtectedDownloadPath(platform.platform_key)}>
Sign in for {platform.platform} access
</Link>
) : (
<div className="button button--ghost button--full is-disabled" aria-disabled="true">
Release URL not configured yet
</div>
)}
</article>
)
})}
</div>
</Section>
<Section title="How the release lane works">
<div className="card-grid">
{desktopDownloadSteps.map((step, index) => (
<article key={step} className="card card--compact">
<p className="status-pill">{index + 1}</p>
<p>{step}</p>
</article>
))}
</div>
</Section>
<Section title="Release posture">
<PublicLaunchStatus title="Desktop release access stays launch-honest" />
</Section>
<Section
title="Browser and desktop responsibilities"
description="The release lane is easier to trust when the site explains why some actions stay public, some stay protected, and the simulator itself stays native."
>
<div className="card-grid">
{deliverySurfaceCards.map((surface) => (
<article key={surface.title} className="card">
<h3>{surface.title}</h3>
<p>{surface.description}</p>
</article>
))}
</div>
</Section>
<Section title="Release integrity and documentation">
<div className="card-grid">
{desktopReleaseSignals.map((signal) => (
<article key={signal.title} className="card">
<h3>{signal.title}</h3>
<p>{signal.description}</p>
</article>
))}
<article className="card">
<h3>Operator rollout references</h3>
<ul className="list">
{releaseManifest.public_docs_url ? (
<li>
Public docs:{' '}
<a href={releaseManifest.public_docs_url} target="_blank" rel="noreferrer">
{releaseManifest.public_docs_url}
</a>
</li>
) : (
<li>Public docs URL not configured yet.</li>
)}
{releaseManifest.release_notes_url ? (
<li>
Release notes:{' '}
<a href={releaseManifest.release_notes_url} target="_blank" rel="noreferrer">
{releaseManifest.release_notes_url}
</a>
</li>
) : (
<li>Release notes URL not configured yet.</li>
)}
<li>
Notices and source posture:{' '}
{releaseManifest.corresponding_source_url ? (
<a href={releaseManifest.corresponding_source_url} target="_blank" rel="noreferrer">
Corresponding source
</a>
) : (
'configure the corresponding-source URL before external launch'
)}
</li>
</ul>
</article>
</div>
</Section>
<Section title="Why this page does not expose raw download URLs">
<article className="callout">
<p>
HyperTwist treats desktop distribution as an account-gated release surface.
Public pages can describe supported targets and release posture, but the actual
download links live behind the protected dashboard where plan and entitlement
state are resolved.
</p>
<Link className="button button--ghost" to="/login">
Sign in to check access
</Link>
</article>
</Section>
<Section title="Pair the desktop app with your browser account">
<article className="callout">
<p>
After sign-in, open the operator dashboard to generate a desktop-link token.
That token is designed to hand browser identity and plan posture over to the local desktop app without exposing your password.
</p>
<Link className="button button--ghost" to="/app">
Open dashboard
</Link>
</article>
</Section>
</MarketingShell>
</>
)
}
export function OpenSourceNoticesPage() {
const releaseManifestQuery = useQuery({
queryKey: ['release-manifest', 'public'],
queryFn: getReleaseManifest,
retry: false,
})
const releaseManifest = useMemo(
() => resolveReleaseManifestView(releaseManifestQuery.data?.manifest, buildPublicReleaseManifestFallback()),
[releaseManifestQuery.data?.manifest],
)
return (
<>
<SiteMetadata
title="HyperTwist open-source notices"
description="Review HyperTwist public open-source notices and corresponding-source guidance for downloadable distribution surfaces."
canonicalPath="/open-source-notices"
/>
<MarketingShell
eyebrow="Legal and notices"
title="Open-source notices for public distribution surfaces."
lede="HyperTwist pricing, checkout, release, and download pages must make legal and corresponding-source guidance visible whenever shipped builds include MPL-covered material."
>
<Section title="Key components">
<div className="card-grid">
{openSourceNotices.map((item) => (
<article className="card" key={item.component}>
<h3>{item.component}</h3>
<p className="status-pill">{item.license}</p>
<p>{item.whyItMatters}</p>
</article>
))}
</div>
</Section>
<Section title="Corresponding source">
<div className="card">
<p>
MPL-covered shipped builds need a stable corresponding-source location for the exact distributed material.
</p>
<ul className="list">
<li>
Public corresponding-source URL:{' '}
{releaseManifest.corresponding_source_url ? (
<a href={releaseManifest.corresponding_source_url} target="_blank" rel="noreferrer">{releaseManifest.corresponding_source_url}</a>
) : (
'configure the public corresponding-source URL before external launch'
)}
</li>
<li>
Public repository / notices reference:{' '}
{releaseManifest.open_source_repo_url ? (
<a href={releaseManifest.open_source_repo_url} target="_blank" rel="noreferrer">{releaseManifest.open_source_repo_url}</a>
) : (
'configure the public repository/notices URL before external launch'
)}
</li>
</ul>
<p>
Official MPL 2.0 license text:{' '}
<a href="https://www.mozilla.org/en-US/MPL/2.0/" target="_blank" rel="noreferrer">
https://www.mozilla.org/en-US/MPL/2.0/
</a>
</p>
</div>
</Section>
<Section title="Distribution readiness">
<PublicLaunchStatus title="Notices and corresponding-source readiness" />
</Section>
<Section
title="Distribution doctrine"
description="These public legal surfaces should explain why the website is part of the release story without pretending it has replaced the native simulator."
>
<div className="card-grid">
{distributionDoctrineCards.map((card) => (
<article className="card" key={card.title}>
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
</MarketingShell>
</>
)
}
export function PrivacyPage() {
return (
<>
<SiteMetadata
title="HyperTwist privacy"
description="Understand the HyperTwist privacy posture for browser account access, desktop-link issuance, and the desktop-first simulator boundary."
canonicalPath="/privacy"
/>
<MarketingShell
eyebrow="Privacy"
title="Privacy posture"
lede="HyperTwist keeps the browser shell narrow and the simulator desktop-first. Privacy descriptions must reflect that separation clearly."
>
<Section title="Core points">
<article className="card">
<ul className="list">
<li>The public website stores account/session data needed for authentication, plan access, and desktop-link issuance.</li>
<li>The browser shell does not claim ownership over the full simulator runtime state unless a future browser-client packet is explicitly opened.</li>
<li>Support, billing, and release operations should collect only the data required to deliver digital access and maintain legal compliance.</li>
</ul>
</article>
</Section>
<Section
title="Practical privacy boundary"
description="Privacy wording should follow the actual product split instead of flattening the browser shell and native simulator into one vague surface."
>
<div className="card-grid">
{privacyBoundaryCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
</MarketingShell>
</>
)
}
export function TermsPage() {
return (
<>
<SiteMetadata
title="HyperTwist terms"
description="Review the HyperTwist terms of access for browser account routes, desktop delivery, and open-source notice obligations."
canonicalPath="/terms"
/>
<MarketingShell
eyebrow="Terms"
title="Terms of access"
lede="HyperTwist access is digital-first and plan-gated. Terms should match the actual delivery and account model."
>
<Section title="Service posture">
<article className="card">
<ul className="list">
<li>Browser access covers public pages, account, release, download, and operator/dashboard surfaces.</li>
<li>The simulator itself is delivered through the desktop lane unless a later browser-client branch is explicitly opened.</li>
<li>Downloaded builds and their public distribution pages remain subject to open-source notice and corresponding-source disclosure rules where applicable.</li>
</ul>
</article>
</Section>
<Section
title="Terms in practice"
description="These terms-oriented boundaries keep the public site, protected dashboard, and desktop simulator aligned with the actual delivery model."
>
<div className="card-grid">
{termsBoundaryCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
</MarketingShell>
</>
)
}
export function ShippingPaymentPage() {
return (
<>
<SiteMetadata
title="HyperTwist shipping and payment"
description="HyperTwist is delivered digitally through account access, release pages, and desktop downloads with Paddle-ready billing posture."
canonicalPath="/shipping-payment"
/>
<MarketingShell
eyebrow="Shipping & payment"
title="Digital delivery only"
lede="HyperTwist is not a physical-goods storefront. Delivery happens through authenticated account access, release pages, and desktop downloads."
>
<Section title="Delivery model">
<article className="card">
<p>{paddleReadyDescription}</p>
<ul className="list">
<li>No physical goods ship through this site.</li>
<li>Pricing and checkout are structured for Paddle-backed digital plans.</li>
<li>Desktop downloads must remain paired with public notices and legal links when required by shipped-code obligations.</li>
</ul>
</article>
</Section>
<Section
title="Digital delivery workflow"
description="A professional digital-delivery lane does more than expose a buy button. It keeps release posture, entitlement, and the desktop handoff coherent."
>
<div className="card-grid">
{digitalDeliveryCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
</MarketingShell>
</>
)
}

View file

@ -0,0 +1,733 @@
import { useDeferredValue, useMemo, useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { ArrowRight, BookOpenText, Boxes, Download, ExternalLink, Landmark, MonitorCog, Sparkles } from 'lucide-react'
import { Link, useSearchParams } from 'react-router-dom'
import { getReleaseManifest } from '../auth/auth-api'
import { MarketingShell } from '../components/layout/MarketingShell'
import { SiteMetadata } from '../components/seo/SiteMetadata'
import { PublicLaunchStatus } from '../components/ui/PublicLaunchStatus'
import { brandConfig } from '../site-config'
import { formatReleasePublishedAt, resolveReleaseManifestView } from '../release-manifest'
import { getReleasePlatformValidationSummary } from '../shared/package-validation'
import {
capabilityPillars,
changelogEntries,
companyNarrative,
deliverySurfaceCards,
deploymentReadinessTracks,
heroMetrics,
higherDimensionalRuntimeGuideCards,
inputAndDevicePostureCards,
operatorManualTracks,
operatorPlaybooks,
publicDocumentationPrinciples,
releaseStoryCards,
resourceCollections,
roadmapHonestyCards,
shippingNowCards,
simulatorManualCards,
supportFaqs,
} from '../site-data'
import { buildPublicReleaseManifestFallback, Section, supportTopicGuidance } from './public-page-helpers'
export function HomeLanding() {
return (
<>
<SiteMetadata
title="HyperTwist"
description="HyperTwist is a native cube and hypercube training environment for recognition, replay, coaching, higher-dimensional runtime ownership, and desktop-first operator workflows."
canonicalPath="/"
/>
<MarketingShell
eyebrow="Desktop-first training. Browser-first operator access."
title="HyperTwist turns cube practice into a real operator-grade training stack."
lede="Recognition, replay, coaching, analytics, higher-dimensional runtime ownership, public distribution, and browser-based account access all live under one honest product story."
>
<section className="hero-panel">
<div className="hero-grid">
<div className="hero-copy">
<p>
HyperTwist is a native training environment for classic cube, higher-dimensional
families, browser-assisted recognition, replay explanation, and desktop packaging.
The public site is intentionally honest: the desktop runtime is real, the browser
account/dashboard is real, and the optional full-browser simulator path remains spec-only.
</p>
<div className="button-row">
<Link className="button button--primary" to="/download">
Download desktop app
</Link>
<Link className="button button--ghost" to="/app">
Open operator dashboard
</Link>
<Link className="button button--ghost" to="/pricing">
View pricing
</Link>
</div>
</div>
<div className="hero-visual-card">
<img
src="/branding/hypertwist-3d-symbol.png"
alt="HyperTwist symbol"
className="hero-visual-card__image"
/>
<p className="hero-visual-card__caption">
Browser account shell outside the simulator. Native Unreal runtime inside the simulator.
</p>
</div>
</div>
<div className="metric-grid">
{heroMetrics.map((metric) => (
<article key={metric.label} className="metric-card">
<strong>{metric.value}</strong>
<span>{metric.label}</span>
</article>
))}
</div>
</section>
<Section
title="Current public site status"
description="The homepage now exposes the same bounded preview-versus-launch truth that governs the release and pricing lanes."
>
<PublicLaunchStatus title="Public website and release posture" />
</Section>
<Section
title="What ships now"
description="The public site only describes current product truth or explicitly marked retained/spec-only branches."
>
<div className="card-grid">
{shippingNowCards.map((item) => (
<article key={item} className="card card--compact">
<Sparkles size={18} />
<p>{item}</p>
</article>
))}
</div>
</Section>
<Section
title="Capability pillars"
description="The page structure borrows the FamiliarOS and ScriptoriumAI public-shell discipline, then rebrands the actual product around HyperTwist runtime authority."
>
<div className="card-grid">
{capabilityPillars.map((pillar) => (
<article key={pillar.title} className="card">
<h3>{pillar.title}</h3>
<p>{pillar.description}</p>
</article>
))}
</div>
</Section>
<Section
title="Roadmap-honest posture"
description="Important boundaries stay visible instead of being blurred into vague marketing claims."
>
<div className="split-grid">
{roadmapHonestyCards.map((item) => (
<article key={item} className="callout">
<p>{item}</p>
</article>
))}
</div>
</Section>
<Section
title="Delivery surfaces"
description="Use the website for account, release, pricing, and notices. Use the desktop runtime for the core simulator."
>
<div className="feature-band">
<article className="feature-band__card">
<MonitorCog size={22} />
<h3>Browser account and operator shell</h3>
<p>Authenticated browser access for release posture, desktop pairing, notices, and operator state.</p>
<Link to="/app" className="inline-link">
Open dashboard <ArrowRight size={15} />
</Link>
</article>
<article className="feature-band__card">
<Download size={22} />
<h3>Desktop download and package lane</h3>
<p>Public download posture for the native Unreal build, with legal linkage already wired in.</p>
<Link to="/download" className="inline-link">
Open download center <ArrowRight size={15} />
</Link>
</article>
<article className="feature-band__card">
<Landmark size={22} />
<h3>Checkout and notices discipline</h3>
<p>Paddle-ready pricing plus public notices surfaces for any downloadable build containing MPL-covered material.</p>
<Link to="/open-source-notices" className="inline-link">
Review notices <ArrowRight size={15} />
</Link>
</article>
</div>
</Section>
<Section
title="Why both browser and desktop stay"
description="The public site owns operator and distribution work the simulator should not dilute, while the simulator stays native for the runtime-heavy training job."
>
<div className="card-grid">
{deliverySurfaceCards.map((surface) => (
<article key={surface.title} className="card">
<h3>{surface.title}</h3>
<p>{surface.description}</p>
<ul className="list top-gap">
{surface.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
</MarketingShell>
</>
)
}
export function AboutPage() {
return (
<>
<SiteMetadata
title="About HyperTwist"
description="Learn why HyperTwist exists, how it treats higher-dimensional training as first-class work, and why the public site stays honest about desktop-first simulator truth."
canonicalPath="/about"
/>
<MarketingShell
eyebrow="Why HyperTwist exists"
title="A training stack serious enough for higher-dimensional cubing."
lede="HyperTwist exists because cubers deserve one coherent system for physical recognition, explanation, practice, replay, analytics, and hyper puzzle ownership."
>
<Section title="Mission">
<div className="card">
<p>{companyNarrative.mission}</p>
<p>{companyNarrative.posture}</p>
<p>{companyNarrative.distribution}</p>
</div>
</Section>
<Section
title="Why the web surface remains necessary"
description="Keeping the website does not weaken the desktop-first thesis. It keeps public distribution, release, and operator-governance work outside the simulator proper."
>
<div className="card-grid">
{deliverySurfaceCards.map((surface) => (
<article key={surface.title} className="card">
<h3>{surface.title}</h3>
<p>{surface.description}</p>
</article>
))}
</div>
</Section>
<Section title="What makes the product different">
<div className="card-grid">
<article className="card">
<Boxes size={22} />
<h3>It treats higher-dimensional puzzles as first-class work</h3>
<p>120-cell and 5D runtime ownership are not hand-wavy aspirations. They are part of the current product truth.</p>
</article>
<article className="card">
<BookOpenText size={22} />
<h3>It stays roadmap-honest</h3>
<p>Shipped, retained, and spec-only surfaces remain clearly separated so public copy matches actual authority.</p>
</article>
<article className="card">
<MonitorCog size={22} />
<h3>It separates browser shell from simulator truth</h3>
<p>The public web surface helps operators access the product without pretending the browser already replaces the desktop runtime.</p>
</article>
</div>
</Section>
</MarketingShell>
</>
)
}
export function ResourcesPage() {
const [query, setQuery] = useState('')
const deferredQuery = useDeferredValue(query)
const windowsValidationSummary = getReleasePlatformValidationSummary('windows')
const filteredCollections = useMemo(() => {
const normalized = deferredQuery.trim().toLowerCase()
if (!normalized) return resourceCollections
return resourceCollections
.map((collection) => ({
...collection,
items: collection.items.filter((item) => item.toLowerCase().includes(normalized) || collection.title.toLowerCase().includes(normalized)),
}))
.filter((collection) => collection.items.length > 0)
}, [deferredQuery])
return (
<>
<SiteMetadata
title="HyperTwist resources"
description="Browse product-safe HyperTwist resources for rollout, release notes, documentation, operator onboarding, and higher-dimensional product truth."
canonicalPath="/resources"
/>
<MarketingShell
eyebrow="Public resources"
title="Resources that explain the product without leaking operator-only internals."
lede="This surface is shaped after the FamiliarOS and ScriptoriumAI public resource pages, but constrained to safe HyperTwist product-facing material."
>
<Section title="Resource finder" description="Search the public resource categories that matter to operators and buyers.">
<label className="input-label" htmlFor="resource-query">Filter resources</label>
<input
id="resource-query"
className="input"
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="Search training, rollout, notices..."
/>
<div className="card-grid top-gap">
{filteredCollections.map((collection) => (
<article key={collection.title} className="card">
<h3>{collection.title}</h3>
<ul className="list">
{collection.items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section title="Direct routes">
<div className="feature-band">
<Link to="/docs" className="feature-band__card feature-band__card--link">
<BookOpenText size={22} />
<h3>Docs landing</h3>
<p>Product-facing documentation, boundaries, and rollout guidance.</p>
</Link>
<Link to="/support" className="feature-band__card feature-band__card--link">
<ExternalLink size={22} />
<h3>Support</h3>
<p>Contact, rollout questions, and account/download help.</p>
</Link>
<Link to="/changelog" className="feature-band__card feature-band__card--link">
<Sparkles size={22} />
<h3>Release notes</h3>
<p>Recent public-facing packets and posture updates.</p>
</Link>
</div>
</Section>
<Section
title="Operator playbooks"
description="These are the public-safe working patterns that matter once a team moves from curiosity into real rollout."
>
<div className="card-grid">
{operatorPlaybooks.map((playbook) => (
<article key={playbook.title} className="card">
<h3>{playbook.title}</h3>
<ul className="list top-gap">
{playbook.steps.map((step) => (
<li key={step}>{step}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section
title="Simulator use today"
description="This summary is deliberately practical: what you actually do in the desktop runtime once browser identity and release posture are already resolved."
>
<div className="card-grid">
{simulatorManualCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section
title="Higher-dimensional runtime guide"
description="These public-safe cards explain which higher-dimensional family lanes are already real and what runtime posture each one actually uses."
>
<div className="card-grid">
{higherDimensionalRuntimeGuideCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section
title="Current control and device posture"
description="This keeps public resources honest about what input/runtime ownership is already strong and what still remains a later native completion packet."
>
<div className="card-grid">
{inputAndDevicePostureCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section
title="Deployment readiness snapshot"
description="The public site can be detailed without becoming misleading when rollout guidance stays separated into identity, package, and legal lanes."
>
<div className="card-grid">
{deploymentReadinessTracks.map((track) => (
<article key={track.title} className="card">
<h3>{track.title}</h3>
<ul className="list top-gap">
{track.steps.map((step) => (
<li key={step}>{step}</li>
))}
</ul>
</article>
))}
</div>
</Section>
{windowsValidationSummary ? (
<Section title="Current packaged desktop proof">
<article className="card">
<h3>{windowsValidationSummary.lane}</h3>
<p>
Latest public-safe package evidence was generated{' '}
{formatReleasePublishedAt(windowsValidationSummary.generated_at) || windowsValidationSummary.generated_at}
{' '}in {windowsValidationSummary.configuration} mode and passed across{' '}
{windowsValidationSummary.smoke_map_count} dedicated-family higher-dimensional training map{windowsValidationSummary.smoke_map_count === 1 ? '' : 's'}.
</p>
<ul className="list top-gap">
{windowsValidationSummary.smoke_maps.map((map) => (
<li key={map.map_url}>
{map.label}: {map.result}
</li>
))}
</ul>
<div className="button-row top-gap">
<Link className="button button--ghost" to="/download">
Open download center
</Link>
<Link className="button button--ghost" to="/app">
Open operator dashboard
</Link>
</div>
</article>
</Section>
) : null}
</MarketingShell>
</>
)
}
export function DocsPage() {
const releaseManifestQuery = useQuery({
queryKey: ['release-manifest', 'public'],
queryFn: getReleaseManifest,
retry: false,
})
const releaseManifest = useMemo(
() => resolveReleaseManifestView(releaseManifestQuery.data?.manifest, buildPublicReleaseManifestFallback()),
[releaseManifestQuery.data?.manifest],
)
return (
<>
<SiteMetadata
title="HyperTwist documentation"
description="Open the HyperTwist public documentation lane for feature-registry-backed product truth, roadmap honesty, and distribution guidance."
canonicalPath="/docs"
/>
<MarketingShell
eyebrow="Documentation posture"
title="HyperTwist documentation stays capability-accurate."
lede="The public docs surface points users to product-safe truth: feature registry discipline, roadmap honesty, release notes, and distribution/legal guidance."
>
<Section title="Public documentation lanes">
<div className="card-grid">
{publicDocumentationPrinciples.map((principle) => (
<article key={principle.title} className="card">
<h3>{principle.title}</h3>
<p>{principle.description}</p>
</article>
))}
</div>
</Section>
<Section
title="Operator manual"
description="This is the public-facing manual for how HyperTwist is actually used today: browser first for identity and release posture, desktop first for the simulator."
>
<div className="card-grid">
{operatorManualTracks.map((track) => (
<article key={track.title} className="card">
<h3>{track.title}</h3>
<p>{track.description}</p>
<ul className="list top-gap">
{track.steps.map((step) => (
<li key={step}>{step}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section
title="Browser versus simulator boundary"
description="The docs stay professional by being explicit about what each surface does better."
>
<div className="card-grid">
{deliverySurfaceCards.map((surface) => (
<article key={surface.title} className="card">
<h3>{surface.title}</h3>
<p>{surface.description}</p>
</article>
))}
</div>
</Section>
<Section
title="Simulator manual"
description="These are the current product-safe usage tracks for the native runtime itself."
>
<div className="card-grid">
{simulatorManualCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section
title="Higher-dimensional family guide"
description="These are the currently represented higher-dimensional lanes and the truthful host/runtime posture for each."
>
<div className="card-grid">
{higherDimensionalRuntimeGuideCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section
title="Deployment readiness manual"
description="This is the public-safe checklist for moving from preview posture to operator-grade rollout."
>
<div className="card-grid">
{deploymentReadinessTracks.map((track) => (
<article key={track.title} className="card">
<h3>{track.title}</h3>
<ul className="list top-gap">
{track.steps.map((step) => (
<li key={step}>{step}</li>
))}
</ul>
</article>
))}
</div>
</Section>
<Section
title="Input and device posture"
description="Public documentation should be explicit about the current control/runtime truth instead of blurring groundwork and finished VR claims together."
>
<div className="card-grid">
{inputAndDevicePostureCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
{releaseManifest.public_docs_url ? (
<Section title="External docs portal">
<a className="button button--primary" href={releaseManifest.public_docs_url} target="_blank" rel="noreferrer">
Open public docs portal
</a>
</Section>
) : null}
</MarketingShell>
</>
)
}
export function SupportPage() {
const [searchParams] = useSearchParams()
const supportTopic = searchParams.get('topic')
const selectedSupportTopic = supportTopic ? supportTopicGuidance[supportTopic] : null
return (
<>
<SiteMetadata
title="HyperTwist support"
description="Get help with HyperTwist rollout, desktop downloads, pricing, legal readiness, and browser-to-desktop operator access."
canonicalPath="/support"
/>
<MarketingShell
eyebrow="Operator help"
title="Support for rollout, downloads, pricing, and browser-to-desktop access."
lede="Support is focused on helping operators understand what the browser shell does, what the desktop build does, and how the two connect."
>
{selectedSupportTopic ? (
<Section title="Selected help lane">
<article className="callout">
<p className="status-pill status-pill--info">{selectedSupportTopic.title}</p>
<p>{selectedSupportTopic.description}</p>
</article>
</Section>
) : null}
<Section title="Contact">
<article className="card">
<p>
Email <a href={brandConfig.contact.emailHref}>{brandConfig.contact.email}</a> for account activation,
release enablement, pricing configuration, or deployment help.
</p>
</article>
</Section>
<Section title="Frequently asked questions">
<div className="card-grid">
{supportFaqs.map((faq) => (
<article className="card" key={faq.question}>
<h3>{faq.question}</h3>
<p>{faq.answer}</p>
</article>
))}
</div>
</Section>
<Section
title="Support lanes"
description="Support works best when operators know whether they need public guidance, protected browser access, or native desktop follow-through."
>
<div className="card-grid">
{operatorPlaybooks.map((playbook) => (
<article key={playbook.title} className="card">
<h3>{playbook.title}</h3>
<ul className="list top-gap">
{playbook.steps.map((step) => (
<li key={step}>{step}</li>
))}
</ul>
</article>
))}
</div>
</Section>
</MarketingShell>
</>
)
}
export function ChangelogPage() {
const releaseManifestQuery = useQuery({
queryKey: ['release-manifest', 'public'],
queryFn: getReleaseManifest,
retry: false,
})
const releaseManifest = useMemo(
() => resolveReleaseManifestView(releaseManifestQuery.data?.manifest, buildPublicReleaseManifestFallback()),
[releaseManifestQuery.data?.manifest],
)
return (
<>
<SiteMetadata
title="HyperTwist release notes"
description="Review recent public-facing HyperTwist changes across browser operator access, package hardening, diagnostics, and distribution readiness."
canonicalPath="/changelog"
/>
<MarketingShell
eyebrow="Release notes"
title="Recent public-facing HyperTwist changes."
lede="This page focuses on user-visible posture changes: browser operator access, package hardening, diagnostics, and distribution readiness."
>
<Section title="Latest changes">
<div className="timeline">
{changelogEntries.map((entry) => (
<article key={`${entry.date}-${entry.title}`} className="timeline-entry">
<p className="timeline-entry__date">{entry.date}</p>
<h3>{entry.title}</h3>
<p>{entry.details}</p>
</article>
))}
</div>
</Section>
<Section
title="How to read the release feed"
description="HyperTwist release notes stay useful when they distinguish simulator/runtime work, browser-operator work, and distribution/legal hardening instead of collapsing them together."
>
<div className="card-grid">
{releaseStoryCards.map((card) => (
<article key={card.title} className="card">
<h3>{card.title}</h3>
<p>{card.description}</p>
<ul className="list top-gap">
{card.bullets.map((bullet) => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</article>
))}
</div>
</Section>
{releaseManifest.release_notes_url ? (
<Section title="External release notes">
<a className="button button--ghost" href={releaseManifest.release_notes_url} target="_blank" rel="noreferrer">
Open release feed
</a>
</Section>
) : null}
</MarketingShell>
</>
)
}

File diff suppressed because it is too large Load diff

View file

@ -2,18 +2,18 @@ import { Suspense, lazy, type ReactNode } from 'react'
import { Route } from 'react-router-dom' import { Route } from 'react-router-dom'
import { GeneralPageLoader } from '../components/ui/Skeletons' import { GeneralPageLoader } from '../components/ui/Skeletons'
const HomeLanding = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.HomeLanding }))) const HomeLanding = lazy(() => import('../pages/public-pages-marketing').then((m) => ({ default: m.HomeLanding })))
const AboutPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.AboutPage }))) const AboutPage = lazy(() => import('../pages/public-pages-marketing').then((m) => ({ default: m.AboutPage })))
const ResourcesPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.ResourcesPage }))) const ResourcesPage = lazy(() => import('../pages/public-pages-marketing').then((m) => ({ default: m.ResourcesPage })))
const DocsPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.DocsPage }))) const DocsPage = lazy(() => import('../pages/public-pages-marketing').then((m) => ({ default: m.DocsPage })))
const SupportPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.SupportPage }))) const SupportPage = lazy(() => import('../pages/public-pages-marketing').then((m) => ({ default: m.SupportPage })))
const ChangelogPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.ChangelogPage }))) const ChangelogPage = lazy(() => import('../pages/public-pages-marketing').then((m) => ({ default: m.ChangelogPage })))
const PricingPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.PricingPage }))) const PricingPage = lazy(() => import('../pages/public-pages-commerce').then((m) => ({ default: m.PricingPage })))
const DownloadPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.DownloadPage }))) const DownloadPage = lazy(() => import('../pages/public-pages-commerce').then((m) => ({ default: m.DownloadPage })))
const OpenSourceNoticesPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.OpenSourceNoticesPage }))) const OpenSourceNoticesPage = lazy(() => import('../pages/public-pages-commerce').then((m) => ({ default: m.OpenSourceNoticesPage })))
const PrivacyPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.PrivacyPage }))) const PrivacyPage = lazy(() => import('../pages/public-pages-commerce').then((m) => ({ default: m.PrivacyPage })))
const TermsPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.TermsPage }))) const TermsPage = lazy(() => import('../pages/public-pages-commerce').then((m) => ({ default: m.TermsPage })))
const ShippingPaymentPage = lazy(() => import('../pages/public-pages').then((m) => ({ default: m.ShippingPaymentPage }))) const ShippingPaymentPage = lazy(() => import('../pages/public-pages-commerce').then((m) => ({ default: m.ShippingPaymentPage })))
const LoginPage = lazy(() => import('../pages/auth-pages').then((m) => ({ default: m.LoginPage }))) const LoginPage = lazy(() => import('../pages/auth-pages').then((m) => ({ default: m.LoginPage })))
const RegisterPage = lazy(() => import('../pages/auth-pages').then((m) => ({ default: m.RegisterPage }))) const RegisterPage = lazy(() => import('../pages/auth-pages').then((m) => ({ default: m.RegisterPage })))

View file

@ -168,6 +168,93 @@ export const simulatorManualCards = [
}, },
] as const ] as const
export const higherDimensionalRuntimeGuideCards = [
{
title: 'Magic120Cell packaged runtime',
description: 'Use the packaged native training lane when you need real 120-cell runtime-state, projection ownership, and persistence-aware family behavior.',
bullets: [
'Launch the dedicated Magic120Cell training map from the desktop runtime.',
'Use the current symmetry, focus, and logical-visibility posture as the authoritative first-party runtime owner.',
'Treat the public website as documentation and rollout support, not as the runtime that executes this family.',
],
},
{
title: 'MagicCube5D packaged runtime',
description: 'Use the packaged native training lane when you need real 5D projection, stereo, focus, and face-visibility ownership.',
bullets: [
'Launch the dedicated MagicCube5D training map from the desktop runtime.',
'Use the current projection-distance, stereo, and visibility defaults as the bounded first-party runtime posture.',
'Keep this lane described honestly as packaged native behavior rather than browser simulator parity.',
],
},
{
title: 'MagicTile embedded-browser runtime',
description: 'Use the embedded browser lane for the current non-Euclidean tiling host posture while native renderer widening remains intentionally gated.',
bullets: [
'Treat the embedded browser/CEF shell as the current shipped host for the live MagicTile interaction lane.',
'Keep shared scramble normalization, timer transport, and state-bridge ownership attached to the first-party native shell and bridge.',
'Do not describe a separate native renderer port as live while the renderer-widening gate stays explicit No-Go.',
],
},
] as const
export const inputAndDevicePostureCards = [
{
title: 'Keyboard and mouse ship today',
description: 'Classic-cube play and bounded keyboard-driven higher-dimensional interaction are already real current product lanes.',
bullets: [
'Classic-cube runtime input already includes click, touch, orbit, zoom, and bounded keyboard move intent.',
'The current classic keyboard profile is the shipped `classic-wca-keyboard/v1` mapping.',
'Treat this as real simulator input ownership, not as a browser-side placeholder.',
],
},
{
title: 'Higher-dimensional view controls are already owned',
description: 'Magic120Cell and MagicCube5D already carry first-party projection, focus, symmetry or stereo, and visibility defaults in the packaged runtime.',
bullets: [
'Magic120Cell keeps symmetry, logical-visibility depth, and center-cell focus posture explicit.',
'MagicCube5D keeps projection-distance, stereo, face-visibility, and focus posture explicit.',
'These controls belong to the dedicated-family desktop runtime lanes, not to the public website.',
],
},
{
title: 'XR groundwork exists, but the full VR lane is not finished',
description: 'EnhancedInput posture and motion-controller groundwork exist, but HyperTwist does not yet market a fully finished OpenXR/controller/rebinding runtime lane.',
bullets: [
'Project config already carries EnhancedInput plus Vive, Oculus Touch, Mixed Reality, and Valve Index axis groundwork.',
'Current product truth should not overclaim headset-specific runtime ownership or polished user-facing input rebinding.',
'A later native XR completion packet still needs dedicated runtime owners, user-facing settings, and Windows package validation with controller truth.',
],
},
] as const
export const deploymentReadinessTracks = [
{
title: '1. Identity and access posture',
steps: [
'Confirm shared browser auth is live before inviting operators into the protected release lane.',
'Use the protected dashboard to resolve plan, entitlement, and desktop-link token posture.',
'Keep browser-to-desktop handoff explicit so the installed app never depends on password reuse.',
],
},
{
title: '2. Release and package posture',
steps: [
'Publish release-manifest truth for the active desktop targets before widening any public launch language.',
'Keep package validation proof visible beside download posture so rollout remains evidence-backed.',
'Differentiate browser-shell changes from simulator/package changes in release notes and support guidance.',
],
},
{
title: '3. Legal and source posture',
steps: [
'Keep notices and corresponding-source links visible anywhere pricing, checkout, or downloads are exposed.',
'Treat public pricing/download surfaces as part of the distributed product, not detached brochure pages.',
'Do not let launch copy outrun the actual checkout, release, or source-availability configuration.',
],
},
] as const
export const publicDocumentationPrinciples = [ export const publicDocumentationPrinciples = [
{ {
title: 'Feature-registry-backed wording only', title: 'Feature-registry-backed wording only',
@ -388,6 +475,15 @@ export const resourceCollections = [
'120-cell and 5D dedicated-family runtime ownership overview', '120-cell and 5D dedicated-family runtime ownership overview',
], ],
}, },
{
title: 'Deployment readiness',
items: [
'Shared auth and browser-to-desktop pairing posture',
'Package validation proof and release-manifest interpretation',
'Pricing, checkout, notices, and corresponding-source coordination',
'Launch-readiness distinction between preview, protected, and public lanes',
],
},
] as const ] as const
export const changelogEntries = [ export const changelogEntries = [
@ -454,6 +550,18 @@ export const supportFaqs = [
question: 'Is the simulator fully in the browser?', question: 'Is the simulator fully in the browser?',
answer: 'No. The current shipping lane is desktop-first and Unreal-backed. The public website offers account, operator, support, and download access, while the optional full-browser simulator path remains spec-only.', answer: 'No. The current shipping lane is desktop-first and Unreal-backed. The public website offers account, operator, support, and download access, while the optional full-browser simulator path remains spec-only.',
}, },
{
question: 'If the desktop app is primary, why keep the web version?',
answer: 'Because the browser shell owns the parts that should stay outside the simulator: public positioning, account access, billing, release posture, download gating, notices, and browser-to-desktop pairing. Keeping that work on the web makes the native runtime easier to trust and easier to operate.',
},
{
question: 'Is VR/controller support already fully finished?',
answer: 'Not yet. HyperTwist already has real EnhancedInput posture and motion-controller groundwork, but it does not yet claim a fully finished OpenXR/controller/runtime or polished rebinding lane.',
},
{
question: 'Can I already customize controls and higher-dimensional view posture?',
answer: 'Partly. The current desktop runtime already owns the classic keyboard profile plus higher-dimensional projection, focus, and visibility defaults, but a broader polished user-facing preferences and rebinding layer is still a later native packet.',
},
{ {
question: 'Can I download a build immediately after sign-in?', question: 'Can I download a build immediately after sign-in?',
answer: 'Yes, once a release URL is configured for your plan. The dashboard also exposes a desktop-link token so the browser account can pair with the desktop app safely.', answer: 'Yes, once a release URL is configured for your plan. The dashboard also exposes a desktop-link token so the browser account can pair with the desktop app safely.',