From 9da661a97481adb668e4cff4468db946946592a0 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Tue, 26 May 2026 18:45:51 +0200 Subject: [PATCH] Implement Phase 6R-AG native capture-route workflow --- .../HyperTwistContractLibrary.cpp | 67 +++ .../HyperTwistHttpSpeechClient.cpp | 12 + .../HyperTwistMockSpeechClient.cpp | 4 + .../HyperTwistTrainingSubsystem.cpp | 448 ++++++++++++++++++ .../HyperTwistRecognitionTypes.h | 335 +++++++++++++ .../HyperTwistTrainingTypes.h | 3 + ...NativeCaptureRouteWorkflowContractTest.cpp | 348 ++++++++++++++ docs/REPO_LICENSE_TRACKING.md | 25 +- ...ONTROL_IMPLEMENTATION_PACKET_2026-05-26.md | 164 +++++++ ...N_CONTROL_PREPARATION_PACKET_2026-05-26.md | 132 ++++++ ...PERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md | 12 +- .../HyperTwist/FEATURE_REGISTRY.md | 6 +- .../HyperTwist/ROADMAP.md | 28 +- 13 files changed, 1559 insertions(+), 25 deletions(-) create mode 100644 UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowContractTest.cpp create mode 100644 docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_IMPLEMENTATION_PACKET_2026-05-26.md create mode 100644 docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_PREPARATION_PACKET_2026-05-26.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp index 59753d0..5f4d0f7 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -958,6 +958,63 @@ namespace HyperTwistContractLibraryInternal return Profile; } + FHyperTwistSpeechNativeCaptureRouteWorkflowProfile + MakeDefaultSpeechNativeCaptureRouteWorkflowProfile(const FString& ProfileId) + { + FHyperTwistSpeechNativeCaptureRouteWorkflowProfile Profile; + Profile.NativeCaptureRouteWorkflowProfileId = ProfileId; + Profile.WorkflowKind = TEXT("first-party-native-capture-route-workflow"); + Profile.OwnershipContractId = TEXT("native-capture-route-ownership-v1"); + Profile.PreparationContractId = TEXT("native-capture-route-preparation-v1"); + Profile.SessionReopenContractId = TEXT("native-capture-route-session-reopen-v1"); + Profile.RouteInspectActionId = TEXT("inspect-native-capture-route"); + Profile.PreparationRetryActionId = TEXT("retry-capture-route"); + Profile.SessionReopenActionId = TEXT("reopen-speech-session"); + Profile.PermissionDependencyActionId = TEXT("resolve-device-permission-workflow"); + Profile.bSupportsRouteInspect = true; + Profile.bSupportsPreparationRetry = true; + Profile.bSupportsSessionReopen = true; + + auto AddPanel = [&Profile]( + const TCHAR* PanelId, + const TCHAR* PanelKind, + const TCHAR* AnchorId, + const bool bVisibleByDefault + ) + { + FHyperTwistSpeechShellPanelLayout Panel; + Panel.PanelId = PanelId; + Panel.PanelKind = PanelKind; + Panel.AnchorId = AnchorId; + Panel.bVisibleByDefault = bVisibleByDefault; + Profile.Panels.Add(Panel); + }; + + auto AddAction = [&Profile]( + const TCHAR* ActionId, + const TCHAR* InputBinding, + const TCHAR* SurfaceId + ) + { + FHyperTwistSpeechShellActionBinding ActionBinding; + ActionBinding.ActionId = ActionId; + ActionBinding.InputBinding = InputBinding; + ActionBinding.SurfaceId = SurfaceId; + Profile.ActionBindings.Add(ActionBinding); + }; + + AddPanel(TEXT("capture-route-ownership-summary-card"), TEXT("capture-route-ownership-summary-card"), TEXT("top-left"), true); + AddPanel(TEXT("capture-route-preparation-card"), TEXT("capture-route-preparation-card"), TEXT("left-stack-below-summary"), true); + AddPanel(TEXT("capture-route-session-reopen-card"), TEXT("capture-route-session-reopen-card"), TEXT("right-stack-aligned-summary"), true); + + AddAction(TEXT("inspect-native-capture-route"), TEXT("I"), TEXT("capture-route-ownership-summary-card")); + AddAction(TEXT("retry-capture-route"), TEXT("R"), TEXT("capture-route-preparation-card")); + AddAction(TEXT("reopen-speech-session"), TEXT("O"), TEXT("capture-route-session-reopen-card")); + AddAction(TEXT("resolve-device-permission-workflow"), TEXT("P"), TEXT("capture-route-ownership-summary-card")); + AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root")); + return Profile; + } + FHyperTwistSpeechUsageCostDashboardShellProfile MakeDefaultSpeechUsageCostDashboardShellProfile( const FString& ProfileId ) @@ -2527,6 +2584,12 @@ FHyperTwistSpeechSessionConfig UHyperTwistContractLibrary::MakeSampleSpeechSessi HyperTwistContractLibraryInternal::MakeDefaultSpeechDevicePermissionWorkflowProfile( Config.DevicePermissionWorkflowProfileId ); + Config.NativeCaptureRouteWorkflowProfileId = + TEXT("speech-native-capture-route-workflow-v1"); + Config.NativeCaptureRouteWorkflowProfileDefinition = + HyperTwistContractLibraryInternal::MakeDefaultSpeechNativeCaptureRouteWorkflowProfile( + Config.NativeCaptureRouteWorkflowProfileId + ); Config.ProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1"); Config.ProviderProfileDefinition = HyperTwistContractLibraryInternal::MakeLocalHttpSpeechProviderProfile( @@ -2707,6 +2770,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistContractLibrary::MakeMockSpeechService TEXT("devicePermissionWorkflow"), TEXT("devicePermissionSettingsHandoff"), TEXT("devicePermissionRecheck"), + TEXT("nativeCaptureRouteWorkflow"), + TEXT("captureRoutePreparationControl"), + TEXT("captureRouteOwnershipAssessment"), + TEXT("reopenSpeechSession"), TEXT("streamingPreview"), TEXT("manualTranscriptCommit"), TEXT("batchedTranscribe"), diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpSpeechClient.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpSpeechClient.cpp index 19a06d6..1433b41 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpSpeechClient.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpSpeechClient.cpp @@ -327,6 +327,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal TEXT("devicePermissionWorkflow"), TEXT("devicePermissionSettingsHandoff"), TEXT("devicePermissionRecheck"), + TEXT("nativeCaptureRouteWorkflow"), + TEXT("captureRoutePreparationControl"), + TEXT("captureRouteOwnershipAssessment"), + TEXT("reopenSpeechSession"), TEXT("streamingPreview"), TEXT("manualTranscriptCommit"), TEXT("batchedTranscribe"), @@ -419,6 +423,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal TEXT("devicePermissionWorkflow"), TEXT("devicePermissionSettingsHandoff"), TEXT("devicePermissionRecheck"), + TEXT("nativeCaptureRouteWorkflow"), + TEXT("captureRoutePreparationControl"), + TEXT("captureRouteOwnershipAssessment"), + TEXT("reopenSpeechSession"), TEXT("streamingPreview"), TEXT("manualTranscriptCommit"), TEXT("batchedTranscribe"), @@ -487,6 +495,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal Health.Capabilities.AddUnique(TEXT("devicePermissionWorkflow")); Health.Capabilities.AddUnique(TEXT("devicePermissionSettingsHandoff")); Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck")); + Health.Capabilities.AddUnique(TEXT("nativeCaptureRouteWorkflow")); + Health.Capabilities.AddUnique(TEXT("captureRoutePreparationControl")); + Health.Capabilities.AddUnique(TEXT("captureRouteOwnershipAssessment")); + Health.Capabilities.AddUnique(TEXT("reopenSpeechSession")); Health.Capabilities.AddUnique(TEXT("streamingPreview")); Health.Capabilities.AddUnique(TEXT("manualTranscriptCommit")); Health.SupportedOrchestrationProfiles.AddUnique(TEXT("python-batch-transcribe-v1")); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockSpeechClient.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockSpeechClient.cpp index 3b808e3..57cd019 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockSpeechClient.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockSpeechClient.cpp @@ -135,6 +135,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistMockSpeechClient::GetSpeechServiceHeal Health.Capabilities.AddUnique(TEXT("devicePermissionWorkflow")); Health.Capabilities.AddUnique(TEXT("devicePermissionSettingsHandoff")); Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck")); + Health.Capabilities.AddUnique(TEXT("nativeCaptureRouteWorkflow")); + Health.Capabilities.AddUnique(TEXT("captureRoutePreparationControl")); + Health.Capabilities.AddUnique(TEXT("captureRouteOwnershipAssessment")); + Health.Capabilities.AddUnique(TEXT("reopenSpeechSession")); Health.Capabilities.AddUnique(TEXT("streamingPreview")); Health.Capabilities.AddUnique(TEXT("manualTranscriptCommit")); Health.Capabilities.AddUnique(TEXT("batchedTranscribe")); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index 5f78ac8..5c26c08 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -2921,6 +2921,11 @@ namespace HyperTwistTrainingSubsystemInternal const FHyperTwistTrainingCompanionSpeechSessionState* PriorState ); + FHyperTwistSpeechNativeCaptureRouteWorkflowState BuildSpeechNativeCaptureRouteWorkflowState( + const FHyperTwistSpeechSessionConfig& SessionConfig, + const FHyperTwistTrainingCompanionSpeechSessionState* PriorState + ); + FHyperTwistSpeechUsageCostDashboardShellState BuildSpeechUsageCostDashboardShellState( const FHyperTwistSpeechSessionConfig& SessionConfig, const FHyperTwistTrainingCompanionSpeechSessionState* PriorState @@ -3064,6 +3069,63 @@ namespace HyperTwistTrainingSubsystemInternal return Profile; } + FHyperTwistSpeechNativeCaptureRouteWorkflowProfile + BuildDefaultSpeechNativeCaptureRouteWorkflowProfile(const FString& ProfileId) + { + FHyperTwistSpeechNativeCaptureRouteWorkflowProfile Profile; + Profile.NativeCaptureRouteWorkflowProfileId = ProfileId; + Profile.WorkflowKind = TEXT("first-party-native-capture-route-workflow"); + Profile.OwnershipContractId = TEXT("native-capture-route-ownership-v1"); + Profile.PreparationContractId = TEXT("native-capture-route-preparation-v1"); + Profile.SessionReopenContractId = TEXT("native-capture-route-session-reopen-v1"); + Profile.RouteInspectActionId = TEXT("inspect-native-capture-route"); + Profile.PreparationRetryActionId = TEXT("retry-capture-route"); + Profile.SessionReopenActionId = TEXT("reopen-speech-session"); + Profile.PermissionDependencyActionId = TEXT("resolve-device-permission-workflow"); + Profile.bSupportsRouteInspect = true; + Profile.bSupportsPreparationRetry = true; + Profile.bSupportsSessionReopen = true; + + auto AddPanel = [&Profile]( + const TCHAR* PanelId, + const TCHAR* PanelKind, + const TCHAR* AnchorId, + const bool bVisibleByDefault + ) + { + FHyperTwistSpeechShellPanelLayout Panel; + Panel.PanelId = PanelId; + Panel.PanelKind = PanelKind; + Panel.AnchorId = AnchorId; + Panel.bVisibleByDefault = bVisibleByDefault; + Profile.Panels.Add(Panel); + }; + + auto AddAction = [&Profile]( + const TCHAR* ActionId, + const TCHAR* InputBinding, + const TCHAR* SurfaceId + ) + { + FHyperTwistSpeechShellActionBinding ActionBinding; + ActionBinding.ActionId = ActionId; + ActionBinding.InputBinding = InputBinding; + ActionBinding.SurfaceId = SurfaceId; + Profile.ActionBindings.Add(ActionBinding); + }; + + AddPanel(TEXT("capture-route-ownership-summary-card"), TEXT("capture-route-ownership-summary-card"), TEXT("top-left"), true); + AddPanel(TEXT("capture-route-preparation-card"), TEXT("capture-route-preparation-card"), TEXT("left-stack-below-summary"), true); + AddPanel(TEXT("capture-route-session-reopen-card"), TEXT("capture-route-session-reopen-card"), TEXT("right-stack-aligned-summary"), true); + + AddAction(TEXT("inspect-native-capture-route"), TEXT("I"), TEXT("capture-route-ownership-summary-card")); + AddAction(TEXT("retry-capture-route"), TEXT("R"), TEXT("capture-route-preparation-card")); + AddAction(TEXT("reopen-speech-session"), TEXT("O"), TEXT("capture-route-session-reopen-card")); + AddAction(TEXT("resolve-device-permission-workflow"), TEXT("P"), TEXT("capture-route-ownership-summary-card")); + AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root")); + return Profile; + } + FHyperTwistSpeechUsageCostDashboardShellProfile BuildDefaultSpeechUsageCostDashboardShellProfile( const FString& ProfileId ) @@ -4554,6 +4616,14 @@ namespace HyperTwistTrainingSubsystemInternal Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck")); } + void ApplySpeechNativeCaptureRouteHealthDefaults(FHyperTwistSpeechServiceHealth& Health) + { + Health.Capabilities.AddUnique(TEXT("nativeCaptureRouteWorkflow")); + Health.Capabilities.AddUnique(TEXT("captureRoutePreparationControl")); + Health.Capabilities.AddUnique(TEXT("captureRouteOwnershipAssessment")); + Health.Capabilities.AddUnique(TEXT("reopenSpeechSession")); + } + bool IsSpeechPermissionError(const FString& ErrorText) { const FString NormalizedError = ErrorText.ToLower(); @@ -4984,6 +5054,283 @@ bool IsSpeechPermissionRecheckError(const FString& ErrorText) ApplySpeechDevicePermissionWorkflowPosture(Profile, SessionState, WorkflowState); } + void AddSpeechNativeCaptureRouteWorkflowIssue( + FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState, + const FHyperTwistSpeechNativeCaptureRouteWorkflowIssue& Issue + ) + { + if (!Issue.IsStructurallyValid()) + { + return; + } + + WorkflowState.Issues.Add(Issue); + if (!WorkflowState.ActiveIssue.IsStructurallyValid()) + { + WorkflowState.ActiveIssue = Issue; + } + } + + void ApplySpeechNativeCaptureRouteWorkflowPosture( + const FHyperTwistSpeechNativeCaptureRouteWorkflowProfile& Profile, + const FHyperTwistTrainingCompanionSpeechSessionState& SessionState, + FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState + ) + { + const FHyperTwistSpeechMicrophoneShellState& ShellState = SessionState.MicrophoneShellState; + const FHyperTwistSpeechDevicePermissionWorkflowState& PermissionWorkflowState = + SessionState.DevicePermissionWorkflowState; + const FHyperTwistSpeechProviderRouteDecision RouteDecision = + SessionState.ServiceHealth.ProviderRouteDecision.IsStructurallyValid() + ? SessionState.ServiceHealth.ProviderRouteDecision + : SessionState.SessionConfig.ProviderRouteDecision; + const FString RouteError = !SessionState.LastError.IsEmpty() + ? SessionState.LastError + : (!ShellState.LastRouteError.IsEmpty() + ? ShellState.LastRouteError + : (!SessionState.ServiceHealth.LastError.IsEmpty() + ? SessionState.ServiceHealth.LastError + : RouteDecision.FailureReason)); + + WorkflowState.CaptureRouteStateId = !ShellState.CaptureRouteStateId.IsEmpty() + ? ShellState.CaptureRouteStateId + : (!RouteDecision.RouteStateId.IsEmpty() ? RouteDecision.RouteStateId : TEXT("route-closed")); + WorkflowState.PermissionStateId = + PermissionWorkflowState.IsStructurallyValid() && !PermissionWorkflowState.PermissionStateId.IsEmpty() + ? PermissionWorkflowState.PermissionStateId + : (!ShellState.PermissionStateId.IsEmpty() + ? ShellState.PermissionStateId + : TEXT("permission-granted")); + WorkflowState.ActiveProviderProfileId = + RouteDecision.IsStructurallyValid() && !RouteDecision.SelectedProviderProfileId.IsEmpty() + ? RouteDecision.SelectedProviderProfileId + : (!SessionState.SessionConfig.ProviderProfileId.IsEmpty() + ? SessionState.SessionConfig.ProviderProfileId + : TEXT("speech-provider/local-http-sidecar-profile-v1")); + WorkflowState.ActiveServiceLaneId = + RouteDecision.IsStructurallyValid() && !RouteDecision.SelectedServiceLaneId.IsEmpty() + ? RouteDecision.SelectedServiceLaneId + : (!SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId.IsEmpty() + ? SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId + : TEXT("speech/python-service")); + WorkflowState.LatestSourceKind = TEXT("capture-route-ready"); + WorkflowState.WorkflowStateId = TEXT("capture-route-workflow-cleared"); + WorkflowState.StatusLine = SessionState.bSessionOpen + ? TEXT("Native capture route owned for the active speech session.") + : TEXT("Open the speech session before native capture-route ownership begins."); + WorkflowState.DetailLine = SessionState.bSessionOpen + ? TEXT("The first-party native capture-route workflow is clear for the bounded speech lane.") + : TEXT("The workflow stays first-party, but session-open control must happen before route ownership can be confirmed."); + WorkflowState.LastRouteError = RouteError; + WorkflowState.WorkflowEntryCount = 0; + WorkflowState.PreparationEntryCount = 0; + WorkflowState.RouteRetryEntryCount = 0; + WorkflowState.SessionReopenEntryCount = 0; + WorkflowState.PermissionDependencyEntryCount = 0; + WorkflowState.bRouteOwnershipConfirmed = SessionState.bSessionOpen; + WorkflowState.bWorkflowReady = SessionState.bSessionOpen; + WorkflowState.bPreparationRequired = false; + WorkflowState.bSessionOpenRequired = false; + WorkflowState.bPermissionWorkflowBlocked = false; + WorkflowState.bRouteInspectVisible = false; + WorkflowState.bPreparationRetryVisible = false; + WorkflowState.bSessionReopenVisible = false; + WorkflowState.Entries.Reset(); + WorkflowState.Issues.Reset(); + WorkflowState.ActiveIssue = FHyperTwistSpeechNativeCaptureRouteWorkflowIssue(); + WorkflowState.ActiveIssueCount = 0; + + if (!SessionState.bSessionOpen) + { + FHyperTwistSpeechNativeCaptureRouteWorkflowEntry Entry; + Entry.EntryId = TEXT("capture-route-session-open-required"); + Entry.SourceKind = TEXT("session-open-required"); + Entry.WorkflowStateId = TEXT("capture-route-session-open-required"); + Entry.CaptureRouteStateId = WorkflowState.CaptureRouteStateId; + Entry.StatusLine = TEXT("Open the speech session before native capture-route ownership begins."); + Entry.DetailLine = + TEXT("The bounded first-party route owner is not active while the speech session is closed. Reopen the session before capture-route assessment continues."); + Entry.RecommendedActionId = Profile.SessionReopenActionId; + Entry.bRequiresSessionOpen = true; + + FHyperTwistSpeechNativeCaptureRouteWorkflowIssue Issue; + Issue.IssueId = TEXT("capture-route-session-open-required"); + Issue.IssueKind = TEXT("session-open-required"); + Issue.StatusLine = Entry.StatusLine; + Issue.DetailLine = Entry.DetailLine; + Issue.RecommendedActionId = Entry.RecommendedActionId; + + WorkflowState.Entries.Add(Entry); + WorkflowState.WorkflowEntryCount = 1; + WorkflowState.SessionReopenEntryCount = 1; + WorkflowState.WorkflowStateId = Entry.WorkflowStateId; + WorkflowState.LatestSourceKind = Entry.SourceKind; + WorkflowState.StatusLine = Entry.StatusLine; + WorkflowState.DetailLine = Entry.DetailLine; + WorkflowState.bRouteOwnershipConfirmed = false; + WorkflowState.bWorkflowReady = false; + WorkflowState.bSessionOpenRequired = true; + WorkflowState.bSessionReopenVisible = Profile.bSupportsSessionReopen; + AddSpeechNativeCaptureRouteWorkflowIssue(WorkflowState, Issue); + } + else if ((PermissionWorkflowState.IsStructurallyValid() + && (!PermissionWorkflowState.bPermissionGranted + || !PermissionWorkflowState.bPermissionWorkflowReady)) + || WorkflowState.PermissionStateId != TEXT("permission-granted")) + { + FHyperTwistSpeechNativeCaptureRouteWorkflowEntry Entry; + Entry.EntryId = TEXT("capture-route-permission-dependency"); + Entry.SourceKind = TEXT("permission-workflow-dependency"); + Entry.WorkflowStateId = TEXT("capture-route-awaiting-permission"); + Entry.CaptureRouteStateId = TEXT("route-waiting-for-permission"); + Entry.StatusLine = + TEXT("Resolve the device-permission workflow before capture-route ownership continues."); + Entry.DetailLine = + TEXT("The first-party capture-route owner is waiting on the bounded permission workflow. Clear permission review, request, or recheck posture before route preparation resumes."); + Entry.RecommendedActionId = Profile.PermissionDependencyActionId; + Entry.bRequiresPermissionWorkflow = true; + + FHyperTwistSpeechNativeCaptureRouteWorkflowIssue Issue; + Issue.IssueId = TEXT("capture-route-permission-dependency"); + Issue.IssueKind = TEXT("permission-workflow-dependency"); + Issue.StatusLine = Entry.StatusLine; + Issue.DetailLine = Entry.DetailLine; + Issue.RecommendedActionId = Entry.RecommendedActionId; + + WorkflowState.Entries.Add(Entry); + WorkflowState.WorkflowEntryCount = 1; + WorkflowState.PermissionDependencyEntryCount = 1; + WorkflowState.WorkflowStateId = Entry.WorkflowStateId; + WorkflowState.CaptureRouteStateId = Entry.CaptureRouteStateId; + WorkflowState.LatestSourceKind = Entry.SourceKind; + WorkflowState.StatusLine = Entry.StatusLine; + WorkflowState.DetailLine = Entry.DetailLine; + WorkflowState.bRouteOwnershipConfirmed = false; + WorkflowState.bWorkflowReady = false; + WorkflowState.bPermissionWorkflowBlocked = true; + AddSpeechNativeCaptureRouteWorkflowIssue(WorkflowState, Issue); + } + else if (WorkflowState.CaptureRouteStateId == TEXT("route-awaiting-endpoint") + || RouteError == TEXT("service-base-url-missing")) + { + FHyperTwistSpeechNativeCaptureRouteWorkflowEntry Entry; + Entry.EntryId = TEXT("capture-route-preparation-required"); + Entry.SourceKind = TEXT("capture-route-preparation-required"); + Entry.WorkflowStateId = TEXT("capture-route-preparation-required"); + Entry.CaptureRouteStateId = TEXT("route-awaiting-endpoint"); + Entry.StatusLine = TEXT("Native capture route needs preparation before ownership can start."); + Entry.DetailLine = RouteError.IsEmpty() + ? TEXT("Inspect the selected route, prepare the local sidecar or device path, then reopen the speech session once the bounded route is ready.") + : FString::Printf( + TEXT("The capture route reported '%s'. Inspect the selected route, prepare the local sidecar or device path, then reopen the speech session."), + *RouteError + ); + Entry.RecommendedActionId = Profile.RouteInspectActionId; + Entry.bRequiresPreparation = true; + + FHyperTwistSpeechNativeCaptureRouteWorkflowIssue Issue; + Issue.IssueId = TEXT("capture-route-preparation-required"); + Issue.IssueKind = TEXT("capture-route-preparation-required"); + Issue.StatusLine = Entry.StatusLine; + Issue.DetailLine = Entry.DetailLine; + Issue.RecommendedActionId = Entry.RecommendedActionId; + + WorkflowState.Entries.Add(Entry); + WorkflowState.WorkflowEntryCount = 1; + WorkflowState.PreparationEntryCount = 1; + WorkflowState.WorkflowStateId = Entry.WorkflowStateId; + WorkflowState.CaptureRouteStateId = Entry.CaptureRouteStateId; + WorkflowState.LatestSourceKind = Entry.SourceKind; + WorkflowState.StatusLine = Entry.StatusLine; + WorkflowState.DetailLine = Entry.DetailLine; + WorkflowState.bRouteOwnershipConfirmed = false; + WorkflowState.bWorkflowReady = false; + WorkflowState.bPreparationRequired = true; + WorkflowState.bRouteInspectVisible = Profile.bSupportsRouteInspect; + AddSpeechNativeCaptureRouteWorkflowIssue(WorkflowState, Issue); + } + else if (WorkflowState.CaptureRouteStateId == TEXT("route-retry-required") + || WorkflowState.CaptureRouteStateId == TEXT("route-health-degraded") + || (RouteDecision.IsStructurallyValid() && !RouteDecision.bRouteReady) + || (IsSpeechRouteError(RouteError) && RouteError != TEXT("no-active-training-run"))) + { + FHyperTwistSpeechNativeCaptureRouteWorkflowEntry Entry; + Entry.EntryId = TEXT("capture-route-retry-required"); + Entry.SourceKind = TEXT("capture-route-retry-required"); + Entry.WorkflowStateId = TEXT("capture-route-retry-required"); + Entry.CaptureRouteStateId = TEXT("route-retry-required"); + Entry.StatusLine = TEXT("Native capture route must be rechecked before ownership resumes."); + Entry.DetailLine = RouteError.IsEmpty() + ? TEXT("The bounded route owner cannot confirm capture readiness yet. Retry the capture route once the sidecar or device path reports healthy again.") + : FString::Printf( + TEXT("The capture route reported '%s'. Retry the route once the sidecar or device path reports healthy again."), + *RouteError + ); + Entry.RecommendedActionId = Profile.PreparationRetryActionId; + Entry.bRequiresRouteRetry = true; + + FHyperTwistSpeechNativeCaptureRouteWorkflowIssue Issue; + Issue.IssueId = TEXT("capture-route-retry-required"); + Issue.IssueKind = TEXT("capture-route-retry-required"); + Issue.StatusLine = Entry.StatusLine; + Issue.DetailLine = Entry.DetailLine; + Issue.RecommendedActionId = Entry.RecommendedActionId; + + WorkflowState.Entries.Add(Entry); + WorkflowState.WorkflowEntryCount = 1; + WorkflowState.RouteRetryEntryCount = 1; + WorkflowState.WorkflowStateId = Entry.WorkflowStateId; + WorkflowState.CaptureRouteStateId = Entry.CaptureRouteStateId; + WorkflowState.LatestSourceKind = Entry.SourceKind; + WorkflowState.StatusLine = Entry.StatusLine; + WorkflowState.DetailLine = Entry.DetailLine; + WorkflowState.bRouteOwnershipConfirmed = false; + WorkflowState.bWorkflowReady = false; + WorkflowState.bPreparationRetryVisible = Profile.bSupportsPreparationRetry; + AddSpeechNativeCaptureRouteWorkflowIssue(WorkflowState, Issue); + } + + WorkflowState.ActiveIssueCount = WorkflowState.Issues.Num(); + } + + void SynchronizeSpeechNativeCaptureRouteWorkflowState( + FHyperTwistTrainingCompanionSpeechSessionState& SessionState + ) + { + FHyperTwistSpeechSessionConfig EffectiveConfig = SessionState.SessionConfig; + if (EffectiveConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty()) + { + EffectiveConfig.NativeCaptureRouteWorkflowProfileId = + TEXT("speech-native-capture-route-workflow-v1"); + } + const FHyperTwistSpeechNativeCaptureRouteWorkflowProfile Profile = + EffectiveConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid() + ? EffectiveConfig.NativeCaptureRouteWorkflowProfileDefinition + : BuildDefaultSpeechNativeCaptureRouteWorkflowProfile( + !EffectiveConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty() + ? EffectiveConfig.NativeCaptureRouteWorkflowProfileId + : TEXT("speech-native-capture-route-workflow-v1") + ); + + if (!SessionState.NativeCaptureRouteWorkflowState.IsStructurallyValid()) + { + SessionState.NativeCaptureRouteWorkflowState = + BuildSpeechNativeCaptureRouteWorkflowState(EffectiveConfig, &SessionState); + } + + FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState = + SessionState.NativeCaptureRouteWorkflowState; + WorkflowState.NativeCaptureRouteWorkflowProfileId = + Profile.NativeCaptureRouteWorkflowProfileId; + WorkflowState.AvailableActionIds.Reset(); + for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings) + { + WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId); + } + + ApplySpeechNativeCaptureRouteWorkflowPosture(Profile, SessionState, WorkflowState); + } + void AddSpeechUsageCostDashboardIssue( FHyperTwistSpeechUsageCostDashboardShellState& ShellState, const FHyperTwistSpeechUsageCostDashboardIssue& Issue @@ -6830,6 +7177,46 @@ bool IsSpeechPermissionRecheckError(const FString& ErrorText) return WorkflowState; } + FHyperTwistSpeechNativeCaptureRouteWorkflowState BuildSpeechNativeCaptureRouteWorkflowState( + const FHyperTwistSpeechSessionConfig& SessionConfig, + const FHyperTwistTrainingCompanionSpeechSessionState* PriorState + ) + { + const FHyperTwistSpeechNativeCaptureRouteWorkflowProfile Profile = + SessionConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid() + ? SessionConfig.NativeCaptureRouteWorkflowProfileDefinition + : BuildDefaultSpeechNativeCaptureRouteWorkflowProfile( + !SessionConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty() + ? SessionConfig.NativeCaptureRouteWorkflowProfileId + : TEXT("speech-native-capture-route-workflow-v1") + ); + + FHyperTwistSpeechNativeCaptureRouteWorkflowState WorkflowState; + WorkflowState.NativeCaptureRouteWorkflowProfileId = + Profile.NativeCaptureRouteWorkflowProfileId; + for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings) + { + WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId); + } + + if (PriorState != nullptr + && PriorState->NativeCaptureRouteWorkflowState.IsStructurallyValid() + && PriorState->NativeCaptureRouteWorkflowState.NativeCaptureRouteWorkflowProfileId + == Profile.NativeCaptureRouteWorkflowProfileId) + { + WorkflowState = PriorState->NativeCaptureRouteWorkflowState; + WorkflowState.NativeCaptureRouteWorkflowProfileId = + Profile.NativeCaptureRouteWorkflowProfileId; + WorkflowState.AvailableActionIds.Reset(); + for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings) + { + WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId); + } + } + + return WorkflowState; + } + FHyperTwistSpeechUsageCostDashboardShellState BuildSpeechUsageCostDashboardShellState( const FHyperTwistSpeechSessionConfig& SessionConfig, const FHyperTwistTrainingCompanionSpeechSessionState* PriorState @@ -12469,6 +12856,11 @@ bool UHyperTwistTrainingSubsystem::OpenActiveCompanionSpeechSession(FString& Out SessionConfig, nullptr ); + ActiveCompanionSpeechSessionState.NativeCaptureRouteWorkflowState = + HyperTwistTrainingSubsystemInternal::BuildSpeechNativeCaptureRouteWorkflowState( + SessionConfig, + nullptr + ); ActiveCompanionSpeechSessionState.UsageCostHistoryExportShellState = HyperTwistTrainingSubsystemInternal::BuildSpeechUsageCostHistoryExportShellState( SessionConfig, @@ -12563,6 +12955,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState( ActiveCompanionSpeechSessionState ); + HyperTwistTrainingSubsystemInternal::SynchronizeSpeechNativeCaptureRouteWorkflowState( + ActiveCompanionSpeechSessionState + ); HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState( ActiveCompanionSpeechSessionState ); @@ -12752,6 +13147,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState( ActiveCompanionSpeechSessionState ); + HyperTwistTrainingSubsystemInternal::SynchronizeSpeechNativeCaptureRouteWorkflowState( + ActiveCompanionSpeechSessionState + ); HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState( ActiveCompanionSpeechSessionState ); @@ -13278,6 +13676,18 @@ FHyperTwistSpeechSessionConfig UHyperTwistTrainingSubsystem::BuildActiveCompanio const bool bUseMockClient = CompanionSpeechClientKind.Equals(TEXT("mock"), ESearchCase::IgnoreCase); if (!HasActiveRun()) { + if (SessionConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty()) + { + SessionConfig.NativeCaptureRouteWorkflowProfileId = + TEXT("speech-native-capture-route-workflow-v1"); + } + if (!SessionConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid()) + { + SessionConfig.NativeCaptureRouteWorkflowProfileDefinition = + HyperTwistTrainingSubsystemInternal::BuildDefaultSpeechNativeCaptureRouteWorkflowProfile( + SessionConfig.NativeCaptureRouteWorkflowProfileId + ); + } HyperTwistTrainingSubsystemInternal::ApplySpeechPayloadCustodyDefaults(SessionConfig); HyperTwistTrainingSubsystemInternal::ApplySpeechProviderProfileDefaults( SessionConfig, @@ -13375,6 +13785,32 @@ FHyperTwistSpeechSessionConfig UHyperTwistTrainingSubsystem::BuildActiveCompanio SessionConfig.DevicePermissionWorkflowProfileDefinition.DevicePermissionWorkflowProfileId = SessionConfig.DevicePermissionWorkflowProfileId; } + if (SessionConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty() + && SessionConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid()) + { + SessionConfig.NativeCaptureRouteWorkflowProfileId = + SessionConfig.NativeCaptureRouteWorkflowProfileDefinition + .NativeCaptureRouteWorkflowProfileId; + } + if (SessionConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty()) + { + SessionConfig.NativeCaptureRouteWorkflowProfileId = + TEXT("speech-native-capture-route-workflow-v1"); + } + if (!SessionConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid()) + { + SessionConfig.NativeCaptureRouteWorkflowProfileDefinition = + HyperTwistTrainingSubsystemInternal::BuildDefaultSpeechNativeCaptureRouteWorkflowProfile( + SessionConfig.NativeCaptureRouteWorkflowProfileId + ); + } + if (SessionConfig.NativeCaptureRouteWorkflowProfileDefinition + .NativeCaptureRouteWorkflowProfileId.IsEmpty()) + { + SessionConfig.NativeCaptureRouteWorkflowProfileDefinition + .NativeCaptureRouteWorkflowProfileId = + SessionConfig.NativeCaptureRouteWorkflowProfileId; + } SessionConfig.bEnableVad = true; if (!SessionConfig.VadPolicy.IsStructurallyValid()) { @@ -13532,12 +13968,18 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth() HyperTwistTrainingSubsystemInternal::ApplySpeechPayloadCustodyHealthDefaults( ActiveCompanionSpeechSessionState.ServiceHealth ); + HyperTwistTrainingSubsystemInternal::ApplySpeechNativeCaptureRouteHealthDefaults( + ActiveCompanionSpeechSessionState.ServiceHealth + ); HyperTwistTrainingSubsystemInternal::SynchronizeSpeechMicrophoneShellState( ActiveCompanionSpeechSessionState ); HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState( ActiveCompanionSpeechSessionState ); + HyperTwistTrainingSubsystemInternal::SynchronizeSpeechNativeCaptureRouteWorkflowState( + ActiveCompanionSpeechSessionState + ); HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState( ActiveCompanionSpeechSessionState ); @@ -13575,12 +14017,18 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth() HyperTwistTrainingSubsystemInternal::ApplySpeechPayloadCustodyHealthDefaults( ActiveCompanionSpeechSessionState.ServiceHealth ); + HyperTwistTrainingSubsystemInternal::ApplySpeechNativeCaptureRouteHealthDefaults( + ActiveCompanionSpeechSessionState.ServiceHealth + ); HyperTwistTrainingSubsystemInternal::SynchronizeSpeechMicrophoneShellState( ActiveCompanionSpeechSessionState ); HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState( ActiveCompanionSpeechSessionState ); + HyperTwistTrainingSubsystemInternal::SynchronizeSpeechNativeCaptureRouteWorkflowState( + ActiveCompanionSpeechSessionState + ); HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState( ActiveCompanionSpeechSessionState ); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h index 2dc9518..70d3abb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h @@ -2097,6 +2097,331 @@ struct FHyperTwistSpeechDevicePermissionWorkflowState } }; +USTRUCT(BlueprintType) +struct FHyperTwistSpeechNativeCaptureRouteWorkflowProfile +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString NativeCaptureRouteWorkflowProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString WorkflowKind = TEXT("first-party-native-capture-route-workflow"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString OwnershipContractId = TEXT("native-capture-route-ownership-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreparationContractId = TEXT("native-capture-route-preparation-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SessionReopenContractId = TEXT("native-capture-route-session-reopen-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RouteInspectActionId = TEXT("inspect-native-capture-route"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreparationRetryActionId = TEXT("retry-capture-route"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SessionReopenActionId = TEXT("reopen-speech-session"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PermissionDependencyActionId = TEXT("resolve-device-permission-workflow"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsRouteInspect = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsPreparationRetry = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsSessionReopen = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Panels; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ActionBindings; + + bool IsStructurallyValid() const + { + if (NativeCaptureRouteWorkflowProfileId.IsEmpty() + || WorkflowKind.IsEmpty() + || OwnershipContractId.IsEmpty() + || PreparationContractId.IsEmpty() + || SessionReopenContractId.IsEmpty() + || RouteInspectActionId.IsEmpty() + || PreparationRetryActionId.IsEmpty() + || SessionReopenActionId.IsEmpty() + || PermissionDependencyActionId.IsEmpty() + || Panels.Num() <= 0 + || ActionBindings.Num() <= 0) + { + return false; + } + + for (const FHyperTwistSpeechShellPanelLayout& Panel : Panels) + { + if (!Panel.IsStructurallyValid()) + { + return false; + } + } + + for (const FHyperTwistSpeechShellActionBinding& ActionBinding : ActionBindings) + { + if (!ActionBinding.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistSpeechNativeCaptureRouteWorkflowEntry +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString EntryId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceKind; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString WorkflowStateId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CaptureRouteStateId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedActionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRequiresSessionOpen = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRequiresPermissionWorkflow = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRequiresPreparation = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRequiresRouteRetry = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bOwnershipConfirmed = false; + + bool IsStructurallyValid() const + { + return !EntryId.IsEmpty() + && !SourceKind.IsEmpty() + && !WorkflowStateId.IsEmpty() + && !CaptureRouteStateId.IsEmpty() + && !StatusLine.IsEmpty() + && !DetailLine.IsEmpty() + && !RecommendedActionId.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistSpeechNativeCaptureRouteWorkflowIssue +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString IssueId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString IssueKind; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedActionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bBlocksCaptureStart = true; + + bool IsStructurallyValid() const + { + return !IssueId.IsEmpty() + && !IssueKind.IsEmpty() + && !StatusLine.IsEmpty() + && !DetailLine.IsEmpty() + && !RecommendedActionId.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistSpeechNativeCaptureRouteWorkflowState +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString NativeCaptureRouteWorkflowProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString WorkflowStateId = TEXT("capture-route-workflow-cleared"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CaptureRouteStateId = TEXT("route-ready"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PermissionStateId = TEXT("permission-granted"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveServiceLaneId = TEXT("speech/python-service"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LatestSourceKind = TEXT("capture-route-ready"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine = TEXT("Native capture route already owned for the bounded speech session."); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine = + TEXT("The first-party native capture-route workflow is clear for the active speech lane."); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LastRouteError; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 WorkflowEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 PreparationEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RouteRetryEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 SessionReopenEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 PermissionDependencyEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRouteOwnershipConfirmed = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bWorkflowReady = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreparationRequired = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSessionOpenRequired = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPermissionWorkflowBlocked = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRouteInspectVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreparationRetryVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSessionReopenVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ActiveIssueCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistSpeechNativeCaptureRouteWorkflowIssue ActiveIssue; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Entries; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Issues; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray AvailableActionIds; + + bool 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; + } + + if (ActiveIssueCount > 0 && !ActiveIssue.IsStructurallyValid()) + { + return false; + } + + for (const FHyperTwistSpeechNativeCaptureRouteWorkflowEntry& Entry : Entries) + { + if (!Entry.IsStructurallyValid()) + { + return false; + } + } + + for (const FHyperTwistSpeechNativeCaptureRouteWorkflowIssue& Issue : Issues) + { + if (!Issue.IsStructurallyValid()) + { + return false; + } + } + + for (const FString& ActionId : AvailableActionIds) + { + if (ActionId.IsEmpty()) + { + return false; + } + } + + return true; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistSpeechModelPayloadDescriptor { @@ -4770,6 +5095,14 @@ struct FHyperTwistSpeechSessionConfig FHyperTwistSpeechDevicePermissionWorkflowProfile DevicePermissionWorkflowProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString NativeCaptureRouteWorkflowProfileId = + TEXT("speech-native-capture-route-workflow-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistSpeechNativeCaptureRouteWorkflowProfile + NativeCaptureRouteWorkflowProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString ProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1"); @@ -4878,6 +5211,8 @@ struct FHyperTwistSpeechSessionConfig || !MicrophoneShellProfileDefinition.IsStructurallyValid() || DevicePermissionWorkflowProfileId.IsEmpty() || !DevicePermissionWorkflowProfileDefinition.IsStructurallyValid() + || NativeCaptureRouteWorkflowProfileId.IsEmpty() + || !NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid() || ProviderProfileId.IsEmpty() || !ProviderProfileDefinition.IsStructurallyValid() || ByokCustodyProfileId.IsEmpty() diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index e0ab373..ab9aac7 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -1943,6 +1943,9 @@ struct FHyperTwistTrainingCompanionSpeechSessionState UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistSpeechDevicePermissionWorkflowState DevicePermissionWorkflowState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistSpeechNativeCaptureRouteWorkflowState NativeCaptureRouteWorkflowState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistSpeechUsageCostDashboardShellState UsageCostDashboardShellState; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowContractTest.cpp new file mode 100644 index 0000000..57ee844 --- /dev/null +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowContractTest.cpp @@ -0,0 +1,348 @@ +// Copyright HyperTwist, Inc. All Rights Reserved. + +#include "Misc/AutomationTest.h" + +#include "Engine/GameInstance.h" +#include "HyperTwistBootstrap/HyperTwistContractLibrary.h" +#include "HyperTwistRecognition/HyperTwistSpeechClient.h" +#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h" +#include "UObject/UnrealType.h" + +#if WITH_AUTOMATION_TESTS + +namespace HyperTwistWhisperCppPhase6RAGTestInternal +{ + FHyperTwistTrainingDeck MakeSpeechDeck() + { + FHyperTwistTrainingDeck Deck; + Deck.DeckId = TEXT("phase6r-ag/whispercpp-native-capture-route-workflow"); + Deck.Title = TEXT("Phase 6R-AG Whisper Native Capture Route Workflow"); + Deck.DeliveryModes = { + EHyperTwistTrainingDeliveryMode::CoachReviewed + }; + + FHyperTwistTrainingCase TrainingCase; + TrainingCase.CaseId = TEXT("phase6r-ag-case"); + TrainingCase.PuzzleId = TEXT("cube/3x3x3"); + TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Sequence; + TrainingCase.PromptLabel = TEXT("Phase 6R-AG Coach Speech"); + TrainingCase.AllowedDeliveryModes = { + EHyperTwistTrainingDeliveryMode::CoachReviewed + }; + Deck.Cases = {TrainingCase}; + return Deck; + } + + void ForceSpeechClientKind(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& ClientKind) + { + if (TrainingSubsystem == nullptr) + { + return; + } + + if (FStrProperty* SpeechClientKindProperty = FindFProperty( + UHyperTwistTrainingSubsystem::StaticClass(), + TEXT("CompanionSpeechClientKind") + )) + { + SpeechClientKindProperty->SetPropertyValue_InContainer(TrainingSubsystem, ClientKind); + } + } + + UHyperTwistTrainingSubsystem* MakeSpeechSubsystem(const FString& SessionId, const FString& ClientKind) + { + UGameInstance* GameInstance = NewObject(GetTransientPackage()); + if (GameInstance == nullptr) + { + return nullptr; + } + + UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject(GameInstance); + if (TrainingSubsystem == nullptr) + { + return nullptr; + } + + ForceSpeechClientKind(TrainingSubsystem, ClientKind); + const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck( + MakeSpeechDeck(), + TEXT("phase6r-ag-user"), + SessionId, + EHyperTwistTrainingDeliveryMode::CoachReviewed + ); + return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr; + } + + FHyperTwistTrainingCompanionSpeechSessionState* ResolveActiveSpeechSessionState( + UHyperTwistTrainingSubsystem* TrainingSubsystem + ) + { + if (TrainingSubsystem == nullptr) + { + return nullptr; + } + + if (FStructProperty* SessionStateProperty = FindFProperty( + UHyperTwistTrainingSubsystem::StaticClass(), + TEXT("ActiveCompanionSpeechSessionState") + )) + { + return SessionStateProperty->ContainerPtrToValuePtr( + TrainingSubsystem + ); + } + + return nullptr; + } + + void ForcePermissionError(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& ErrorText) + { + if (FHyperTwistTrainingCompanionSpeechSessionState* SessionState = + ResolveActiveSpeechSessionState(TrainingSubsystem)) + { + SessionState->LastError = ErrorText; + SessionState->ServiceHealth.LastError = ErrorText; + } + } + + UHyperTwistHttpSpeechClient* ResolveHttpSpeechClient(UHyperTwistTrainingSubsystem* TrainingSubsystem) + { + if (TrainingSubsystem == nullptr) + { + return nullptr; + } + + FString CloseError; + TrainingSubsystem->CloseActiveCompanionSpeechSession(CloseError); + + if (FObjectPropertyBase* ClientProperty = FindFProperty( + UHyperTwistTrainingSubsystem::StaticClass(), + TEXT("ActiveCompanionSpeechClientObject") + )) + { + return Cast( + ClientProperty->GetObjectPropertyValue_InContainer(TrainingSubsystem) + ); + } + + return nullptr; + } + + void ForcePreparationState( + UHyperTwistTrainingSubsystem* TrainingSubsystem, + const FString& SessionId + ) + { + if (FHyperTwistTrainingCompanionSpeechSessionState* SessionState = + ResolveActiveSpeechSessionState(TrainingSubsystem)) + { + SessionState->bSessionOpen = true; + SessionState->ActiveSessionId = SessionId; + SessionState->OpenedAtUtc = FDateTime::UtcNow().ToIso8601(); + SessionState->LastUpdatedAtUtc = SessionState->OpenedAtUtc; + SessionState->SessionConfig = UHyperTwistContractLibrary::MakeSampleSpeechSessionConfig(); + SessionState->SessionConfig.SessionId = SessionId; + SessionState->LastError = TEXT("service-base-url-missing"); + } + } +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowSessionConfigTest, + "HyperTwist.Permissive.WhisperCpp.Phase6R.AG.NativeCaptureRouteWorkflowSessionConfig", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowSessionConfigTest::RunTest( + const FString& Parameters +) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistWhisperCppPhase6RAGTestInternal::MakeSpeechSubsystem( + TEXT("phase6r-ag-config-session"), + TEXT("mock") + ); + TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AG."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + FString OpenError; + TestTrue(TEXT("The native capture-route workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError)); + TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty()); + + const FHyperTwistTrainingCompanionSpeechSessionState SessionState = + TrainingSubsystem->GetActiveCompanionSpeechSessionState(); + const FHyperTwistSpeechNativeCaptureRouteWorkflowProfile& Profile = + SessionState.SessionConfig.NativeCaptureRouteWorkflowProfileDefinition; + + TestTrue(TEXT("The native capture-route workflow route must expose a structurally valid profile."), Profile.IsStructurallyValid()); + TestEqual(TEXT("The workflow profile must preserve the first-party profile id."), Profile.NativeCaptureRouteWorkflowProfileId, TEXT("speech-native-capture-route-workflow-v1")); + TestEqual(TEXT("The workflow profile must preserve the workflow kind."), Profile.WorkflowKind, TEXT("first-party-native-capture-route-workflow")); + TestEqual(TEXT("The workflow profile must preserve the ownership contract id."), Profile.OwnershipContractId, TEXT("native-capture-route-ownership-v1")); + TestEqual(TEXT("The workflow profile must preserve the preparation contract id."), Profile.PreparationContractId, TEXT("native-capture-route-preparation-v1")); + TestEqual(TEXT("The workflow profile must preserve the reopen contract id."), Profile.SessionReopenContractId, TEXT("native-capture-route-session-reopen-v1")); + TestEqual(TEXT("The workflow profile must preserve the route inspect action id."), Profile.RouteInspectActionId, TEXT("inspect-native-capture-route")); + TestEqual(TEXT("The workflow profile must preserve the retry action id."), Profile.PreparationRetryActionId, TEXT("retry-capture-route")); + TestEqual(TEXT("The workflow profile must preserve the reopen action id."), Profile.SessionReopenActionId, TEXT("reopen-speech-session")); + TestEqual(TEXT("The workflow profile must preserve the permission dependency action id."), Profile.PermissionDependencyActionId, TEXT("resolve-device-permission-workflow")); + TestTrue(TEXT("The speech service health must expose native capture-route workflow capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("nativeCaptureRouteWorkflow"))); + TestTrue(TEXT("The speech service health must expose capture-route preparation control capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("captureRoutePreparationControl"))); + TestTrue(TEXT("The speech service health must expose capture-route ownership assessment capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("captureRouteOwnershipAssessment"))); + TestTrue(TEXT("The speech service health must expose speech-session reopen capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("reopenSpeechSession"))); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowClearedStateTest, + "HyperTwist.Permissive.WhisperCpp.Phase6R.AG.NativeCaptureRouteWorkflowClearedState", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowClearedStateTest::RunTest( + const FString& Parameters +) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistWhisperCppPhase6RAGTestInternal::MakeSpeechSubsystem( + TEXT("phase6r-ag-cleared-session"), + TEXT("mock") + ); + TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AG."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + FString OpenError; + TestTrue(TEXT("The native capture-route workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError)); + TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty()); + + const FHyperTwistTrainingCompanionSpeechSessionState SessionState = + TrainingSubsystem->GetActiveCompanionSpeechSessionState(); + const FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState = + SessionState.NativeCaptureRouteWorkflowState; + + TestTrue(TEXT("The cleared native capture-route workflow state must be structurally valid."), WorkflowState.IsStructurallyValid()); + TestEqual(TEXT("The cleared workflow must preserve the cleared workflow state id."), WorkflowState.WorkflowStateId, TEXT("capture-route-workflow-cleared")); + TestEqual(TEXT("The cleared workflow must preserve route-ready posture."), WorkflowState.CaptureRouteStateId, TEXT("route-ready")); + TestEqual(TEXT("The cleared workflow must preserve permission-granted posture."), WorkflowState.PermissionStateId, TEXT("permission-granted")); + TestEqual(TEXT("The cleared workflow must preserve the ready source kind."), WorkflowState.LatestSourceKind, TEXT("capture-route-ready")); + TestTrue(TEXT("The cleared workflow must confirm route ownership."), WorkflowState.bRouteOwnershipConfirmed); + TestTrue(TEXT("The cleared workflow must preserve workflow-ready posture."), WorkflowState.bWorkflowReady); + TestFalse(TEXT("The cleared workflow must not require preparation."), WorkflowState.bPreparationRequired); + TestFalse(TEXT("The cleared workflow must not require session reopen."), WorkflowState.bSessionOpenRequired); + TestFalse(TEXT("The cleared workflow must not block on permission workflow."), WorkflowState.bPermissionWorkflowBlocked); + TestEqual(TEXT("The cleared workflow must not surface active issues."), WorkflowState.ActiveIssueCount, 0); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowPermissionDependencyTest, + "HyperTwist.Permissive.WhisperCpp.Phase6R.AG.NativeCaptureRouteWorkflowPermissionDependency", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowPermissionDependencyTest::RunTest( + const FString& Parameters +) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistWhisperCppPhase6RAGTestInternal::MakeSpeechSubsystem( + TEXT("phase6r-ag-permission-session"), + TEXT("mock") + ); + TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AG."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + FString OpenError; + TestTrue(TEXT("The native capture-route workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError)); + TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty()); + + HyperTwistWhisperCppPhase6RAGTestInternal::ForcePermissionError( + TrainingSubsystem, + TEXT("microphone-permission-required") + ); + TrainingSubsystem->RefreshCompanionSpeechServiceHealthForAutomation(); + + const FHyperTwistTrainingCompanionSpeechSessionState SessionState = + TrainingSubsystem->GetActiveCompanionSpeechSessionState(); + const FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState = + SessionState.NativeCaptureRouteWorkflowState; + + TestTrue(TEXT("The permission-dependent native capture-route workflow state must remain structurally valid."), WorkflowState.IsStructurallyValid()); + TestEqual(TEXT("The permission-dependent workflow must preserve the waiting workflow state id."), WorkflowState.WorkflowStateId, TEXT("capture-route-awaiting-permission")); + TestEqual(TEXT("The permission-dependent workflow must preserve the waiting capture-route posture."), WorkflowState.CaptureRouteStateId, TEXT("route-waiting-for-permission")); + TestEqual(TEXT("The permission-dependent workflow must preserve permission-required posture."), WorkflowState.PermissionStateId, TEXT("permission-required")); + TestEqual(TEXT("The permission-dependent workflow must preserve the permission-dependency source kind."), WorkflowState.LatestSourceKind, TEXT("permission-workflow-dependency")); + TestFalse(TEXT("The permission-dependent workflow must clear ownership confirmation."), WorkflowState.bRouteOwnershipConfirmed); + TestFalse(TEXT("The permission-dependent workflow must block workflow-ready posture."), WorkflowState.bWorkflowReady); + TestTrue(TEXT("The permission-dependent workflow must block on the permission workflow."), WorkflowState.bPermissionWorkflowBlocked); + TestEqual(TEXT("The permission-dependent workflow must retain one workflow entry."), WorkflowState.WorkflowEntryCount, 1); + TestEqual(TEXT("The permission-dependent workflow must retain one permission-dependency entry."), WorkflowState.PermissionDependencyEntryCount, 1); + TestEqual(TEXT("The permission-dependent workflow must classify the active issue correctly."), WorkflowState.ActiveIssue.IssueKind, TEXT("permission-workflow-dependency")); + TestEqual(TEXT("The permission-dependent workflow must recommend the bounded permission workflow action."), WorkflowState.ActiveIssue.RecommendedActionId, TEXT("resolve-device-permission-workflow")); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowPreparationStateTest, + "HyperTwist.Permissive.WhisperCpp.Phase6R.AG.NativeCaptureRouteWorkflowPreparationState", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowPreparationStateTest::RunTest( + const FString& Parameters +) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistWhisperCppPhase6RAGTestInternal::MakeSpeechSubsystem( + TEXT("phase6r-ag-preparation-session"), + TEXT("http-sidecar") + ); + TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AG."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + UHyperTwistHttpSpeechClient* HttpSpeechClient = + HyperTwistWhisperCppPhase6RAGTestInternal::ResolveHttpSpeechClient(TrainingSubsystem); + TestNotNull(TEXT("The provider-backed speech client must be available for the preparation workflow test."), HttpSpeechClient); + if (HttpSpeechClient == nullptr) + { + return false; + } + + HttpSpeechClient->ServiceBaseUrl.Reset(); + HyperTwistWhisperCppPhase6RAGTestInternal::ForcePreparationState( + TrainingSubsystem, + TEXT("phase6r-ag-preparation-session") + ); + TrainingSubsystem->RefreshCompanionSpeechServiceHealthForAutomation(); + + const FHyperTwistTrainingCompanionSpeechSessionState SessionState = + TrainingSubsystem->GetActiveCompanionSpeechSessionState(); + const FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState = + SessionState.NativeCaptureRouteWorkflowState; + + TestTrue(TEXT("The preparation-required native capture-route workflow state must remain structurally valid."), WorkflowState.IsStructurallyValid()); + TestEqual(TEXT("The preparation-required workflow must preserve the preparation workflow state id."), WorkflowState.WorkflowStateId, TEXT("capture-route-preparation-required")); + TestEqual(TEXT("The preparation-required workflow must preserve the awaiting-endpoint route posture."), WorkflowState.CaptureRouteStateId, TEXT("route-awaiting-endpoint")); + TestEqual(TEXT("The preparation-required workflow must preserve the preparation source kind."), WorkflowState.LatestSourceKind, TEXT("capture-route-preparation-required")); + TestFalse(TEXT("The preparation-required workflow must clear ownership confirmation."), WorkflowState.bRouteOwnershipConfirmed); + TestFalse(TEXT("The preparation-required workflow must block workflow-ready posture."), WorkflowState.bWorkflowReady); + TestTrue(TEXT("The preparation-required workflow must report preparation required."), WorkflowState.bPreparationRequired); + TestTrue(TEXT("The preparation-required workflow must surface route inspection."), WorkflowState.bRouteInspectVisible); + TestEqual(TEXT("The preparation-required workflow must retain one workflow entry."), WorkflowState.WorkflowEntryCount, 1); + TestEqual(TEXT("The preparation-required workflow must retain one preparation entry."), WorkflowState.PreparationEntryCount, 1); + TestEqual(TEXT("The preparation-required workflow must classify the active issue correctly."), WorkflowState.ActiveIssue.IssueKind, TEXT("capture-route-preparation-required")); + TestEqual(TEXT("The preparation-required workflow must carry the missing-endpoint route error."), WorkflowState.LastRouteError, TEXT("service-base-url-missing")); + return true; +} + +#endif diff --git a/docs/REPO_LICENSE_TRACKING.md b/docs/REPO_LICENSE_TRACKING.md index 8ce0716..4227b89 100644 --- a/docs/REPO_LICENSE_TRACKING.md +++ b/docs/REPO_LICENSE_TRACKING.md @@ -215,9 +215,14 @@ Status update on `2026-05-21`: pass is now consumed - the bounded first-party `Phase 6R-AF` real device-permission workflow packet is now landed in current code -- the current next bounded move is a source-backed `Phase 6R-AG` first-party native - capture-route ownership and workflow preparation/control pass, not a new restrictive packet by - default +- the generic source-backed `Phase 6R-AG` first-party native capture-route ownership and + workflow preparation/control pass is now consumed +- the bounded first-party `Phase 6R-AG` native capture-route ownership and workflow + preparation/control packet is now landed in current code +- the current next bounded move is not another restrictive packet by default; if a new + speech-adjacent first-party packet is justified, keep actual OS permission-grant execution, + actual payment execution, provider-portal ownership, and actual payload shipping separately + deferred - use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A` ownership contract for the current queue after that correction @@ -366,8 +371,8 @@ Current practical interpretation: - the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md` - the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md` - the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md` -- the next bounded move is a source-backed `Phase 6R-AG` first-party native capture-route - ownership and workflow preparation/control pass +- the bounded first-party `Phase 6R-AG` native capture-route ownership and workflow + preparation/control packet is now landed in current code Companion docs: @@ -1647,10 +1652,16 @@ Approved working posture: workflow metadata above the existing transcript-session, shell, permission/readiness, payload-custody, provider-custody, provider-routing, usage/cost, history/export, receipt-review, settlement, and exception/handoff seams +- the next bounded first-party implementation slice is now landed as native capture-route + ownership and workflow preparation/control metadata above the existing transcript-session, + shell, permission/readiness, permission-workflow, payload-custody, provider-custody, + provider-routing, usage/cost, history/export, receipt-review, settlement, and + exception/handoff seams - the top-level provider/session contract remains first-party HyperTwist-owned and provider-neutral; this row does not own that lane -- keep native capture-route ownership, actual payment execution, provider-portal ownership, - actual payload shipping, and future voice-asset review separate from the code-license judgment +- keep unrestricted low-level audio-device takeover, actual payment execution, + provider-portal ownership, actual payload shipping, and future voice-asset review separate from + the code-license judgment - treat `SYSTRAN/faster-whisper` as the landed complementary Python-orchestration donor rather than widening `whisper.cpp` into the runtime core diff --git a/docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_IMPLEMENTATION_PACKET_2026-05-26.md b/docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_IMPLEMENTATION_PACKET_2026-05-26.md new file mode 100644 index 0000000..6f24e23 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_IMPLEMENTATION_PACKET_2026-05-26.md @@ -0,0 +1,164 @@ +# HyperTwist Phase 6R-AG first-party native capture-route ownership and workflow preparation/control implementation packet + +Created on `2026-05-26` + +## Status + +- first-party HyperTwist packet +- bounded `Phase 6R-AG` implementation slice + +## Purpose + +This packet lands the next bounded first-party slice above the landed speech +session, microphone-shell, permission/readiness shell, permission-workflow, +payload-custody, provider-custody, provider-routing, normalized usage/cost +event, operator-facing usage/cost dashboard, provider usage/cost +history/export, provider receipt-review, provider billing-settlement, and +provider settlement-exception seams. + +The landed slice is: + +- first-party native capture-route ownership and workflow preparation/control + +It is not: + +- an unrestricted low-level audio-device takeover packet +- an actual OS permission-grant execution packet +- an actual payment execution packet +- a provider-portal ownership packet +- an actual payload-shipping packet +- a broad assistant-platform packet + +## Current authority basis + +This implementation packet stands on: + +- `docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md` +- `docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_PREPARATION_PACKET_2026-05-26.md` +- `docs/arch/HYPERTWIST_PHASE6R_AF_FIRST_PARTY_REAL_DEVICE_PERMISSION_WORKFLOW_IMPLEMENTATION_PACKET_2026-05-25.md` +- `docs/arch/HYPERTWIST_PHASE6R_V_WHISPER_CPP_DEVICE_PERMISSION_AND_CAPTURE_ROUTE_READINESS_SHELL_IMPLEMENTATION_PACKET_2026-05-24.md` + +The preserved owners do not change: + +- first-party HyperTwist remains the top-level provider/session owner +- first-party HyperTwist remains the usage/cost governance owner +- first-party HyperTwist remains the bounded native capture-route workflow + owner for this slice +- `ggml-org/whisper.cpp` remains bounded to offline STT donor seams +- `SYSTRAN/faster-whisper` remains bounded to complementary Python + orchestration/service-lane seams + +## Landed scope + +The current code now owns a bounded native capture-route workflow seam +through: + +- retained recognition contract types for: + - `FHyperTwistSpeechNativeCaptureRouteWorkflowProfile` + - `FHyperTwistSpeechNativeCaptureRouteWorkflowEntry` + - `FHyperTwistSpeechNativeCaptureRouteWorkflowIssue` + - `FHyperTwistSpeechNativeCaptureRouteWorkflowState` + - expanded `FHyperTwistSpeechSessionConfig` + - expanded `FHyperTwistTrainingCompanionSpeechSessionState` +- contract-library defaults for: + - native capture-route workflow profile ids and definitions + - workflow panel ids and action bindings + - service-health capability exposure +- active companion session-config, service-health, and derived native + capture-route workflow composition in: + - `UHyperTwistTrainingSubsystem` +- direct speech-client health capability exposure in: + - `UHyperTwistHttpSpeechClient` + - `UHyperTwistMockSpeechClient` +- focused automation coverage in: + - `HyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowContractTest.cpp` + +## Why this is still intentionally bounded + +This packet lands the bounded native capture-route ownership assessment and +workflow preparation/control family only. + +Still deferred: + +- unrestricted low-level audio-device takeover beyond the landed bounded + workflow/control posture +- actual OS permission-grant execution ownership +- actual payment execution or provider-portal ownership +- actual downloadable model/payload shipping +- broad assistant-platform scope + +## Validation + +Build validation: + +- `UnrealHyperTwistEditor Win64 Development` + +Focused automation validation: + +- `HyperTwist.Permissive.WhisperCpp.Phase6R.AG` `4/4` + +Regression automation validation: + +- `HyperTwist.Permissive.WhisperCpp.Phase6R.AF` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.AE` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.V` `3/3` + +Non-blocking warnings stayed limited to the existing Unreal headless/editor +noise, the pre-existing plugin dependency warning on `UnrealMCP`, the headless +CEF/web-browser warning, and the known `http:/vision/health` plus +`http:/speech/health` hostname-resolution warnings in provider-backed +automation. + +## Queue effect + +This packet consumes the current `Phase 6R-AG` implementation slice. + +`ggml-org/whisper.cpp` remains only partially incorporated: + +- landed now: + - speech transcript session boundary + - live microphone shell boundary + - device-permission and capture-route readiness shell posture + - first-party real device-permission workflow boundary + - first-party native capture-route ownership assessment and workflow + preparation/control boundary for: + - session-open-required posture + - permission-workflow dependency posture + - preparation-required posture + - route-retry posture + - bounded capability exposure for: + - `nativeCaptureRouteWorkflow` + - `captureRoutePreparationControl` + - `captureRouteOwnershipAssessment` + - `reopenSpeechSession` + - downloadable model and payload custody boundary + - first-party provider-profile and BYOK custody boundary + - first-party provider routing and workflow-policy boundary + - first-party normalized usage/cost event-model boundary + - first-party operator-facing provider usage/cost dashboard shell boundary + - first-party provider usage/cost history/export shell boundary + - first-party provider receipt review and posted-charge inspection shell + boundary + - first-party provider billing settlement and invoice reconciliation shell + boundary + - first-party provider settlement exception and external-portal handoff shell + boundary +- still deferred: + - unrestricted low-level audio-device takeover beyond the landed bounded + workflow/control posture + - actual OS permission-grant execution ownership + - actual payment execution or provider-portal ownership + - actual downloadable model/payload shipping + - broad assistant-platform scope + +The next clean move is not another restrictive packet by default. + +If a follow-on speech-adjacent packet is later justified, keep these guards +visible: + +- prove a narrower remaining native capture-route ownership gap before widening + into actual OS permission-grant execution +- keep actual payment execution and provider-portal ownership separate from any + such packet +- keep actual payload shipping separate from usage/cost governance unless a + narrower first-party gap is proven first diff --git a/docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_PREPARATION_PACKET_2026-05-26.md b/docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_PREPARATION_PACKET_2026-05-26.md new file mode 100644 index 0000000..3e62aff --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_PREPARATION_PACKET_2026-05-26.md @@ -0,0 +1,132 @@ +# HyperTwist Phase 6R-AG first-party native capture-route ownership and workflow preparation/control preparation packet + +Created on `2026-05-26` + +## Status + +- first-party HyperTwist packet +- source-backed `Phase 6R-AG` preparation/control slice + +## Purpose + +This packet scopes the next bounded first-party slice above the landed speech +session, microphone-shell, permission/readiness shell, permission-workflow, +payload-custody, provider-custody, provider-routing, normalized usage/cost +event, operator-facing usage/cost dashboard, provider usage/cost +history/export, provider receipt-review, provider billing-settlement, and +provider settlement-exception seams. + +The granted slice is: + +- first-party native capture-route ownership and workflow preparation/control + only + +It is not: + +- an unrestricted low-level audio-device takeover packet +- an actual OS permission-grant execution packet +- an actual payment execution packet +- a provider-portal ownership packet +- an actual payload-shipping packet +- a broad assistant-platform packet + +## Current authority basis + +This control pass stands on: + +- `docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md` +- `docs/arch/HYPERTWIST_PHASE6R_AF_FIRST_PARTY_REAL_DEVICE_PERMISSION_WORKFLOW_IMPLEMENTATION_PACKET_2026-05-25.md` +- `docs/arch/HYPERTWIST_PHASE6R_V_WHISPER_CPP_DEVICE_PERMISSION_AND_CAPTURE_ROUTE_READINESS_SHELL_IMPLEMENTATION_PACKET_2026-05-24.md` +- `docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md` + +The preserved owners do not change here: + +- first-party HyperTwist remains the top-level provider/session owner +- first-party HyperTwist remains the usage/cost governance owner +- first-party HyperTwist remains the bounded native capture-route workflow + owner for this slice +- `ggml-org/whisper.cpp` remains bounded to offline STT donor seams +- `SYSTRAN/faster-whisper` remains bounded to complementary Python + orchestration/service-lane seams + +## Granted family + +The bounded `Phase 6R-AG` slice may land: + +1. first-party native capture-route workflow profile ids and definitions +2. derived native capture-route workflow entry, issue, and state record shapes +3. session-config and companion-session-state expansion for those shapes +4. bounded capability exposure for: + - `nativeCaptureRouteWorkflow` + - `captureRoutePreparationControl` + - `captureRouteOwnershipAssessment` + - `reopenSpeechSession` +5. focused automation for: + - cleared ownership posture + - permission-workflow dependency posture + - preparation-required posture + - route/session reopen posture + +The packet must stay out of: + +- unrestricted low-level audio-device takeover +- actual OS permission-grant execution ownership +- actual payment execution +- provider-portal ownership +- actual payload download or redistribution ownership + +## Proposed implementation shape + +Land the narrower first-party boundary through: + +- retained recognition contract types for: + - native capture-route workflow profile + - native capture-route workflow entry + - native capture-route workflow issue + - native capture-route workflow state + - expanded speech session config + - expanded companion speech session state +- contract-library defaults for: + - native capture-route workflow profile ids and definitions + - action bindings and panel ids + - service-health capability exposure +- training-subsystem composition for: + - session-open-required posture + - permission-workflow dependency posture + - preparation-required posture + - capture-route retry posture + - explicit separation between bounded workflow control and unrestricted + low-level audio-device takeover + - explicit separation between bounded workflow control and actual OS + permission-grant execution +- direct speech-client health capability exposure for: + - `nativeCaptureRouteWorkflow` + - `captureRoutePreparationControl` + - `captureRouteOwnershipAssessment` + - `reopenSpeechSession` + +## Validation target + +Validate with: + +- Unreal build for `UnrealHyperTwistEditor Win64 Development` +- focused automation: + - `HyperTwist.Permissive.WhisperCpp.Phase6R.AG` +- regressions: + - `HyperTwist.Permissive.WhisperCpp.Phase6R.AF` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.AE` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.V` + +## Queue effect + +If this packet lands cleanly, the speech-adjacent queue should still stay +narrow. + +If another first-party speech packet is later justified, it must prove a +narrower remaining native capture-route ownership gap before widening into: + +- actual OS permission-grant execution +- actual payment execution +- provider-portal ownership +- actual payload shipping +- broad assistant-platform scope diff --git a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md index 2567b41..ff89507 100644 --- a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md +++ b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md @@ -192,15 +192,17 @@ The next bounded move is now: control pass is now consumed 38. the bounded first-party `Phase 6R-AF` real device-permission workflow packet is now landed in current code -39. the next bounded move is a source-backed `Phase 6R-AG` first-party native - capture-route ownership and workflow preparation/control pass -40. keep the speech-lane guard visible: +39. the generic source-backed `Phase 6R-AG` first-party native capture-route ownership and + workflow preparation/control pass is now consumed +40. the bounded first-party `Phase 6R-AG` native capture-route ownership and workflow + preparation/control packet is now landed in current code +41. keep the speech-lane guard visible: - keep code-license judgments separate from model, voice, and payload-license review -41. keep the provider-neutral speech-lane guard visible: +42. keep the provider-neutral speech-lane guard visible: - first-party HyperTwist owns the top-level provider/session contract - `whisper.cpp` and `faster-whisper` may win narrower donor slices without inheriting that lane, the provider routing/policy lane, or the usage/cost governance lane -40. keep the `MagicTile` guard visible: +43. keep the `MagicTile` guard visible: - keep broad non-Euclidean interaction or WinForms/OpenTK host-shell ownership closed by default unless a narrower first-party gap is proven above the landed macro-remapping seam diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md index e852e8f..7fa3f6b 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md @@ -131,7 +131,8 @@ Do not collapse those three tiers into one undifferentiated "features" voice. | First-party provider receipt review and posted-charge inspection shell | Implemented now | landed first-party `Phase 6R-AC` | First-party receipt-review shell profiles, derived receipt-review entries, charge-pending and variance issue posture, and bounded inspection capability exposure are now live above the landed provider usage/cost history/export seam. | | First-party provider billing settlement and invoice reconciliation shell | Implemented now | landed first-party `Phase 6R-AD` | First-party settlement shell profiles, derived settlement entries, invoice-reconciliation posture, settlement-readiness issue state, and bounded settlement capability exposure are now live above the landed provider receipt-review seam. | | First-party provider settlement exception and external-portal handoff shell | Implemented now | landed first-party `Phase 6R-AE` | First-party settlement-exception shell profiles, derived exception entries, reference-only external-portal handoff posture, and bounded exception capability exposure are now live above the landed provider settlement seam. | -| First-party real device-permission workflow | Implemented now | landed first-party `Phase 6R-AF` | First-party permission-workflow profiles, request/settings/recheck posture, and bounded workflow capability exposure are now live above the landed permission/readiness shell seam. | +| First-party real device-permission workflow | Implemented now | landed first-party `Phase 6R-AF` | First-party permission-workflow profiles, request/settings/recheck posture, and bounded workflow capability exposure are now live above the landed permission/readiness shell seam. The bounded first-party native capture-route workflow/control layer now lives in landed `Phase 6R-AG`. | +| First-party native capture-route ownership and workflow preparation/control | Implemented now | landed first-party `Phase 6R-AG` | First-party native capture-route ownership assessment, preparation/retry/reopen posture, and bounded workflow capability exposure are now live above the landed permission-workflow seam. Actual OS permission-grant execution, payment execution, provider-portal ownership, and payload shipping remain deferred. | | Analytics/reporting surfaces | Implemented now | landed analytics/reporting packets | Reporting is real, but bounded to accepted retained slices. | | Browser/spatial/media adjunct surfaces | Implemented now | landed `three.js`, `react-three-fiber`, `xr`, `model-viewer`, `remotion` packets | These are implemented bounded families, not proof of unlimited browser-shell parity. | @@ -211,7 +212,8 @@ repo. | Speech transcript session boundary | Implemented now | landed `whisper.cpp` packet | Current bounded STT truth. | | Live microphone capture shell | Implemented now | landed `whisper.cpp` `Phase 6R-U` packet | Current bounded microphone shell truth above the landed transcript-session and orchestration seams. | | Device-permission and capture-route readiness shell | Implemented now | landed `whisper.cpp` `Phase 6R-V` packet | Current bounded permission/readiness shell posture, route fallback issue objects, and explicit retry/request semantics above the landed microphone shell seam. | -| First-party real device-permission workflow | Implemented now | landed first-party `Phase 6R-AF` packet | Current bounded permission-workflow profile ids/definitions, request/settings/recheck posture, and workflow capability exposure are live above the landed permission/readiness shell seam. Native capture-route ownership remains deferred. | +| First-party real device-permission workflow | Implemented now | landed first-party `Phase 6R-AF` packet | Current bounded permission-workflow profile ids/definitions, request/settings/recheck posture, and workflow capability exposure are live above the landed permission/readiness shell seam. The bounded first-party native capture-route workflow/control layer now lives in landed `Phase 6R-AG`. | +| First-party native capture-route ownership and workflow preparation/control | Implemented now | landed first-party `Phase 6R-AG` packet | Current bounded native capture-route ownership assessment, preparation/retry/reopen posture, and workflow capability exposure are live above the landed permission-workflow seam. Actual OS permission-grant execution, payment execution, provider-portal ownership, and payload shipping remain deferred. | | Downloadable model and payload custody boundary | Implemented now | landed `whisper.cpp` `Phase 6R-W` packet | Current bounded custody profile, required/supported payload descriptors, and health-surface integrity metadata above the landed transcript-session and shell seams. | | Provider-backed speech session health and transcript envelopes | Implemented now | first-party speech client surfaces | Current first-party speech seam. | | Python transcription-service orchestration profile | Implemented now | landed `faster-whisper` packet | Current bounded Python STT orchestration truth above the existing speech session boundary. | diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md index 6dd280d..6cd67ca 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -191,9 +191,14 @@ Canonical discovery surfaces for roadmap interpretation: pass is now consumed - the bounded first-party `Phase 6R-AF` real device-permission workflow packet is now landed in current code -- the current next bounded move is a source-backed `Phase 6R-AG` first-party native - capture-route ownership and workflow preparation/control pass, while keeping actual payment - execution, provider-portal ownership, and actual payload shipping separately deferred +- the generic source-backed `Phase 6R-AG` first-party native capture-route ownership and + workflow preparation/control pass is now consumed +- the bounded first-party `Phase 6R-AG` native capture-route ownership and workflow + preparation/control packet is now landed in current code +- the current next bounded move is not another restrictive packet by default; if a new + speech-adjacent first-party packet is justified, keep actual OS permission-grant execution, + actual payment execution, provider-portal ownership, and actual payload shipping separately + deferred - the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than waiting on another first-party packet @@ -280,8 +285,8 @@ Current routing truth: - `brianpeiris/RiftSketch` - the v6.3 CSV label `not_live_reference_or_discard_candidate` is not the final state by itself; read the closed `0R-E` packet for the retained-versus-discarded split -The next bounded move is a source-backed `Phase 6R-AG` first-party native -capture-route ownership and workflow preparation/control pass. +The bounded first-party `Phase 6R-AG` native capture-route ownership and +workflow preparation/control pass is now landed in current code. Queue interpretation after that control pass: @@ -411,8 +416,8 @@ Queue interpretation after that control pass: - provider settlement-exception review - still deferred: - actual payment execution or provider-portal ownership - - real device-permission workflow - - native capture-route ownership beyond bounded shell posture + - actual OS permission-grant execution ownership + - native capture-route ownership beyond the landed bounded workflow/control posture - actual downloadable model or payload shipping - broad assistant-platform scope - `SYSTRAN/faster-whisper` is now landed as a partially incorporated row rather than a generic @@ -458,15 +463,16 @@ Queue interpretation after that control pass: - viseme / gesture runtime integration - broad assistant-platform scope - the next queue shape is now: - - first-party native capture-route ownership and workflow assessment above the + - bounded first-party native capture-route ownership and workflow assessment/control above the landed speech session, shell, permission/readiness, permission-workflow, payload-custody, provider-custody, provider-routing, normalized usage/cost, operator-facing dashboard, history/export, receipt-review, settlement, and exception/handoff seams - the next bounded move should stay narrow: - - a source-backed `Phase 6R-AG` control pass before any widening into actual - payment execution, provider-portal ownership, actual payload shipping, - broad voice-output ownership, or broad assistant-platform scope + - if a new speech-adjacent first-party packet is justified, prove a narrower remaining native + capture-route ownership gap before any widening into actual OS permission-grant execution, + actual payment execution, provider-portal ownership, actual payload shipping, broad + voice-output ownership, or broad assistant-platform scope - keep the speech-input / voice sidecar legal sequencing guard visible: - keep code-license judgments separate from model, voice, and payload-license review - keep the provider-neutral speech-lane guard visible: