From fa0dab8942e9144512b4eaa84cc9b44a0723b5b3 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Mon, 25 May 2026 18:22:08 +0200 Subject: [PATCH] Implement Phase 6R-AC provider receipt review shell --- .../HyperTwistContractLibrary.cpp | 78 ++- .../HyperTwistHttpSpeechClient.cpp | 13 +- .../HyperTwistMockSpeechClient.cpp | 3 + .../HyperTwistTrainingSubsystem.cpp | 494 +++++++++++++++++- .../HyperTwistRecognitionTypes.h | 391 ++++++++++++++ .../HyperTwistTrainingTypes.h | 3 + ...ProviderReceiptReviewShellContractTest.cpp | 285 ++++++++++ docs/REPO_LICENSE_TRACKING.md | 12 +- ...ECTION_IMPLEMENTATION_PACKET_2026-05-25.md | 168 ++++++ ...NSPECTION_PREPARATION_PACKET_2026-05-25.md | 135 +++++ ...PERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md | 14 +- ...NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md | 9 +- .../HyperTwist/FEATURE_REGISTRY.md | 2 + .../HyperTwist/ROADMAP.md | 37 +- 14 files changed, 1613 insertions(+), 31 deletions(-) create mode 100644 UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistWhisperCppPhase6RACProviderReceiptReviewShellContractTest.cpp create mode 100644 docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_IMPLEMENTATION_PACKET_2026-05-25.md create mode 100644 docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_PREPARATION_PACKET_2026-05-25.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp index be799d4..2dcff02 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -1024,6 +1024,68 @@ namespace HyperTwistContractLibraryInternal return Profile; } + FHyperTwistSpeechProviderReceiptReviewShellProfile + MakeDefaultSpeechProviderReceiptReviewShellProfile(const FString& ProfileId) + { + FHyperTwistSpeechProviderReceiptReviewShellProfile Profile; + Profile.ReceiptReviewShellProfileId = ProfileId; + Profile.ShellKind = TEXT("operator-provider-receipt-review"); + Profile.ReceiptSurfaceMode = TEXT("posted-charge-review"); + Profile.VarianceSurfaceMode = TEXT("estimate-versus-posted-charge"); + Profile.RefreshActionId = TEXT("refresh-provider-receipt-review"); + Profile.PostedChargeInspectActionId = TEXT("inspect-provider-posted-charges"); + Profile.VarianceReviewActionId = TEXT("review-provider-charge-variance"); + Profile.RouteInspectActionId = TEXT("inspect-provider-route"); + Profile.BudgetInspectActionId = TEXT("review-provider-budget"); + Profile.RetainedReceiptEntryLimit = 12; + Profile.bSupportsReceiptTimeline = true; + Profile.bSupportsPostedChargeInspection = true; + Profile.bSupportsVarianceReview = true; + Profile.bSupportsEstimateOnlyFallback = true; + Profile.bPreservesFirstPartyReceiptInspectionOwnership = 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("receipt-timeline-card"), TEXT("receipt-timeline-card"), TEXT("top-left"), true); + AddPanel(TEXT("posted-charge-summary-card"), TEXT("posted-charge-summary-card"), TEXT("left-stack-below-receipts"), true); + AddPanel(TEXT("variance-review-card"), TEXT("variance-review-card"), TEXT("top-right"), true); + AddPanel(TEXT("receipt-readiness-card"), TEXT("receipt-readiness-card"), TEXT("right-stack-below-variance"), true); + + AddAction(TEXT("refresh-provider-receipt-review"), TEXT("F7"), TEXT("receipt-timeline-card")); + AddAction(TEXT("inspect-provider-posted-charges"), TEXT("I"), TEXT("posted-charge-summary-card")); + AddAction(TEXT("review-provider-charge-variance"), TEXT("V"), TEXT("variance-review-card")); + AddAction(TEXT("inspect-provider-route"), TEXT("P"), TEXT("receipt-readiness-card")); + AddAction(TEXT("review-provider-budget"), TEXT("B"), TEXT("receipt-readiness-card")); + AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root")); + return Profile; + } + FHyperTwistSpeechModelPayloadDescriptor MakeSpeechModelPayloadDescriptor( const TCHAR* PayloadId, const TCHAR* PayloadKind, @@ -1108,7 +1170,10 @@ namespace HyperTwistContractLibraryInternal TEXT("providerRouteCostDiagnostics"), TEXT("providerUsageCostHistoryShell"), TEXT("providerUsageCostExportPreview"), - TEXT("providerUsageCostSummaryCopy") + TEXT("providerUsageCostSummaryCopy"), + TEXT("providerReceiptReviewShell"), + TEXT("providerPostedChargeInspection"), + TEXT("providerChargeVarianceReview") }; } @@ -2340,6 +2405,12 @@ FHyperTwistSpeechSessionConfig UHyperTwistContractLibrary::MakeSampleSpeechSessi HyperTwistContractLibraryInternal::MakeDefaultSpeechUsageCostHistoryExportShellProfile( Config.UsageCostHistoryExportShellProfileId ); + Config.ProviderReceiptReviewShellProfileId = + TEXT("speech-provider-receipt-review-shell-v1"); + Config.ProviderReceiptReviewShellProfileDefinition = + HyperTwistContractLibraryInternal::MakeDefaultSpeechProviderReceiptReviewShellProfile( + Config.ProviderReceiptReviewShellProfileId + ); Config.PayloadCustodyProfileId = TEXT("downloadable-model-payload-custody-v1"); Config.PayloadCustodyProfileDefinition = HyperTwistContractLibraryInternal::MakeDefaultSpeechPayloadCustodyProfile( @@ -2453,7 +2524,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistContractLibrary::MakeMockSpeechService TEXT("providerRouteCostDiagnostics"), TEXT("providerUsageCostHistoryShell"), TEXT("providerUsageCostExportPreview"), - TEXT("providerUsageCostSummaryCopy") + TEXT("providerUsageCostSummaryCopy"), + TEXT("providerReceiptReviewShell"), + TEXT("providerPostedChargeInspection"), + TEXT("providerChargeVarianceReview") }; Health.SupportedOrchestrationProfiles = { TEXT("python-batch-transcribe-v1") diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpSpeechClient.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpSpeechClient.cpp index f0a6870..4358df5 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpSpeechClient.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpSpeechClient.cpp @@ -347,7 +347,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal TEXT("providerRouteCostDiagnostics"), TEXT("providerUsageCostHistoryShell"), TEXT("providerUsageCostExportPreview"), - TEXT("providerUsageCostSummaryCopy") + TEXT("providerUsageCostSummaryCopy"), + TEXT("providerReceiptReviewShell"), + TEXT("providerPostedChargeInspection"), + TEXT("providerChargeVarianceReview") }; Health.SupportedOrchestrationProfiles = { TEXT("python-batch-transcribe-v1") @@ -427,7 +430,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal TEXT("providerRouteCostDiagnostics"), TEXT("providerUsageCostHistoryShell"), TEXT("providerUsageCostExportPreview"), - TEXT("providerUsageCostSummaryCopy") + TEXT("providerUsageCostSummaryCopy"), + TEXT("providerReceiptReviewShell"), + TEXT("providerPostedChargeInspection"), + TEXT("providerChargeVarianceReview") }; Health.SupportedOrchestrationProfiles = { TEXT("python-batch-transcribe-v1") @@ -492,6 +498,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal Health.Capabilities.AddUnique(TEXT("providerUsageCostHistoryShell")); Health.Capabilities.AddUnique(TEXT("providerUsageCostExportPreview")); Health.Capabilities.AddUnique(TEXT("providerUsageCostSummaryCopy")); + Health.Capabilities.AddUnique(TEXT("providerReceiptReviewShell")); + Health.Capabilities.AddUnique(TEXT("providerPostedChargeInspection")); + Health.Capabilities.AddUnique(TEXT("providerChargeVarianceReview")); Health.SupportedOrchestrationProfiles.AddUnique(TEXT("python-batch-transcribe-v1")); if (Health.ProviderProfileId.IsEmpty()) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockSpeechClient.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockSpeechClient.cpp index 5363014..f0c265b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockSpeechClient.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockSpeechClient.cpp @@ -152,6 +152,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistMockSpeechClient::GetSpeechServiceHeal Health.Capabilities.AddUnique(TEXT("providerUsageCostHistoryShell")); Health.Capabilities.AddUnique(TEXT("providerUsageCostExportPreview")); Health.Capabilities.AddUnique(TEXT("providerUsageCostSummaryCopy")); + Health.Capabilities.AddUnique(TEXT("providerReceiptReviewShell")); + Health.Capabilities.AddUnique(TEXT("providerPostedChargeInspection")); + Health.Capabilities.AddUnique(TEXT("providerChargeVarianceReview")); Health.SupportedOrchestrationProfiles.AddUnique(TEXT("python-batch-transcribe-v1")); return Health; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index f89243a..e74629f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -2926,6 +2926,11 @@ namespace HyperTwistTrainingSubsystemInternal const FHyperTwistTrainingCompanionSpeechSessionState* PriorState ); + FHyperTwistSpeechProviderReceiptReviewShellState BuildSpeechProviderReceiptReviewShellState( + const FHyperTwistSpeechSessionConfig& SessionConfig, + const FHyperTwistTrainingCompanionSpeechSessionState* PriorState + ); + FHyperTwistSpeechMicrophoneShellProfile BuildDefaultCoachCommandMicrophoneShellProfile(const FString& ProfileId) { FHyperTwistSpeechMicrophoneShellProfile Profile; @@ -3109,6 +3114,68 @@ namespace HyperTwistTrainingSubsystemInternal return Profile; } + FHyperTwistSpeechProviderReceiptReviewShellProfile + BuildDefaultSpeechProviderReceiptReviewShellProfile(const FString& ProfileId) + { + FHyperTwistSpeechProviderReceiptReviewShellProfile Profile; + Profile.ReceiptReviewShellProfileId = ProfileId; + Profile.ShellKind = TEXT("operator-provider-receipt-review"); + Profile.ReceiptSurfaceMode = TEXT("posted-charge-review"); + Profile.VarianceSurfaceMode = TEXT("estimate-versus-posted-charge"); + Profile.RefreshActionId = TEXT("refresh-provider-receipt-review"); + Profile.PostedChargeInspectActionId = TEXT("inspect-provider-posted-charges"); + Profile.VarianceReviewActionId = TEXT("review-provider-charge-variance"); + Profile.RouteInspectActionId = TEXT("inspect-provider-route"); + Profile.BudgetInspectActionId = TEXT("review-provider-budget"); + Profile.RetainedReceiptEntryLimit = 12; + Profile.bSupportsReceiptTimeline = true; + Profile.bSupportsPostedChargeInspection = true; + Profile.bSupportsVarianceReview = true; + Profile.bSupportsEstimateOnlyFallback = true; + Profile.bPreservesFirstPartyReceiptInspectionOwnership = 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("receipt-timeline-card"), TEXT("receipt-timeline-card"), TEXT("top-left"), true); + AddPanel(TEXT("posted-charge-summary-card"), TEXT("posted-charge-summary-card"), TEXT("left-stack-below-receipts"), true); + AddPanel(TEXT("variance-review-card"), TEXT("variance-review-card"), TEXT("top-right"), true); + AddPanel(TEXT("receipt-readiness-card"), TEXT("receipt-readiness-card"), TEXT("right-stack-below-variance"), true); + + AddAction(TEXT("refresh-provider-receipt-review"), TEXT("F7"), TEXT("receipt-timeline-card")); + AddAction(TEXT("inspect-provider-posted-charges"), TEXT("I"), TEXT("posted-charge-summary-card")); + AddAction(TEXT("review-provider-charge-variance"), TEXT("V"), TEXT("variance-review-card")); + AddAction(TEXT("inspect-provider-route"), TEXT("P"), TEXT("receipt-readiness-card")); + AddAction(TEXT("review-provider-budget"), TEXT("B"), TEXT("receipt-readiness-card")); + AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root")); + return Profile; + } + FHyperTwistSpeechModelPayloadDescriptor MakeSpeechModelPayloadDescriptor( const TCHAR* PayloadId, const TCHAR* PayloadKind, @@ -3193,7 +3260,10 @@ namespace HyperTwistTrainingSubsystemInternal TEXT("providerRouteCostDiagnostics"), TEXT("providerUsageCostHistoryShell"), TEXT("providerUsageCostExportPreview"), - TEXT("providerUsageCostSummaryCopy") + TEXT("providerUsageCostSummaryCopy"), + TEXT("providerReceiptReviewShell"), + TEXT("providerPostedChargeInspection"), + TEXT("providerChargeVarianceReview") }; } @@ -3868,6 +3938,26 @@ namespace HyperTwistTrainingSubsystemInternal } SessionConfig.UsageCostHistoryExportShellProfileId = SessionConfig.UsageCostHistoryExportShellProfileDefinition.HistoryExportShellProfileId; + if (SessionConfig.ProviderReceiptReviewShellProfileId.IsEmpty() + && SessionConfig.ProviderReceiptReviewShellProfileDefinition.IsStructurallyValid()) + { + SessionConfig.ProviderReceiptReviewShellProfileId = + SessionConfig.ProviderReceiptReviewShellProfileDefinition.ReceiptReviewShellProfileId; + } + if (SessionConfig.ProviderReceiptReviewShellProfileId.IsEmpty()) + { + SessionConfig.ProviderReceiptReviewShellProfileId = + TEXT("speech-provider-receipt-review-shell-v1"); + } + if (!SessionConfig.ProviderReceiptReviewShellProfileDefinition.IsStructurallyValid()) + { + SessionConfig.ProviderReceiptReviewShellProfileDefinition = + BuildDefaultSpeechProviderReceiptReviewShellProfile( + SessionConfig.ProviderReceiptReviewShellProfileId + ); + } + SessionConfig.ProviderReceiptReviewShellProfileId = + SessionConfig.ProviderReceiptReviewShellProfileDefinition.ReceiptReviewShellProfileId; } void ApplySpeechProviderProfileHealthDefaults( @@ -4203,6 +4293,9 @@ namespace HyperTwistTrainingSubsystemInternal Health.Capabilities.AddUnique(TEXT("providerUsageCostHistoryShell")); Health.Capabilities.AddUnique(TEXT("providerUsageCostExportPreview")); Health.Capabilities.AddUnique(TEXT("providerUsageCostSummaryCopy")); + Health.Capabilities.AddUnique(TEXT("providerReceiptReviewShell")); + Health.Capabilities.AddUnique(TEXT("providerPostedChargeInspection")); + Health.Capabilities.AddUnique(TEXT("providerChargeVarianceReview")); } bool IsSpeechPermissionError(const FString& ErrorText) @@ -5043,6 +5136,339 @@ namespace HyperTwistTrainingSubsystemInternal } } + float GetSpeechProviderReceiptVarianceToleranceUsd() + { + return 0.01f; + } + + FString BuildSpeechProviderReceiptReviewSourceKind( + const FHyperTwistSpeechUsageCostHistoryEntry& HistoryEntry + ) + { + if (HistoryEntry.SourceKind.Equals(TEXT("provider-fallback-health"), ESearchCase::IgnoreCase)) + { + return TEXT("provider-fallback-health"); + } + if (HistoryEntry.bProviderReceiptPosted) + { + return TEXT("provider-posted-charge"); + } + return TEXT("history-derived-estimate-only"); + } + + FHyperTwistSpeechProviderReceiptReviewEntry BuildSpeechProviderReceiptReviewEntry( + const FHyperTwistSpeechUsageCostHistoryEntry& HistoryEntry, + const FString& ChargeWindowId + ) + { + FHyperTwistSpeechProviderReceiptReviewEntry Entry; + Entry.ReceiptEntryId = FString::Printf( + TEXT("provider-receipt-review/%s"), + *HistoryEntry.HistoryEntryId + ); + Entry.SourceKind = BuildSpeechProviderReceiptReviewSourceKind(HistoryEntry); + Entry.HistoryEntryId = HistoryEntry.HistoryEntryId; + Entry.UsageEventId = HistoryEntry.UsageEventId; + Entry.CostEventId = HistoryEntry.CostEventId; + Entry.ProviderProfileId = HistoryEntry.ProviderProfileId; + Entry.ServiceLaneId = HistoryEntry.ServiceLaneId; + Entry.PricingSourceId = HistoryEntry.bProviderReceiptPosted + ? TEXT("provider-posted-charge") + : TEXT("first-party-estimate"); + Entry.ChargeWindowId = ChargeWindowId.IsEmpty() ? TEXT("current-session") : ChargeWindowId; + Entry.CapturedAtUtc = HistoryEntry.CapturedAtUtc; + Entry.EstimatedCostUsd = FMath::Max(0.0f, HistoryEntry.EstimatedCostUsd); + Entry.ReportedCostUsd = FMath::Max(0.0f, HistoryEntry.ReportedCostUsd); + Entry.bHasProviderPostedCharge = HistoryEntry.bProviderReceiptPosted; + Entry.bEstimateOnly = HistoryEntry.bEstimateOnly; + if (Entry.bHasProviderPostedCharge) + { + Entry.AbsoluteVarianceUsd = FMath::Abs(Entry.ReportedCostUsd - Entry.EstimatedCostUsd); + Entry.bVarianceWithinTolerance = + Entry.AbsoluteVarianceUsd <= GetSpeechProviderReceiptVarianceToleranceUsd(); + Entry.bRequiresManualInspection = !Entry.bVarianceWithinTolerance; + if (Entry.bVarianceWithinTolerance) + { + Entry.VarianceDirectionId = TEXT("no-variance"); + } + else + { + Entry.VarianceDirectionId = Entry.ReportedCostUsd >= Entry.EstimatedCostUsd + ? TEXT("reported-above-estimate") + : TEXT("reported-below-estimate"); + } + } + else + { + Entry.AbsoluteVarianceUsd = 0.0f; + Entry.VarianceDirectionId = TEXT("provider-charge-pending"); + Entry.bVarianceWithinTolerance = true; + Entry.bRequiresManualInspection = false; + } + + return Entry; + } + + void AddSpeechProviderReceiptReviewIssue( + FHyperTwistSpeechProviderReceiptReviewShellState& ShellState, + const FHyperTwistSpeechProviderReceiptReviewIssue& Issue + ) + { + if (!Issue.IsStructurallyValid()) + { + return; + } + + ShellState.Issues.Add(Issue); + if (!ShellState.ActiveIssue.IsStructurallyValid()) + { + ShellState.ActiveIssue = Issue; + } + } + + void ApplySpeechProviderReceiptReviewPosture( + const FHyperTwistSpeechProviderReceiptReviewShellProfile& Profile, + const FHyperTwistTrainingCompanionSpeechSessionState& SessionState, + FHyperTwistSpeechProviderReceiptReviewShellState& ShellState + ) + { + ShellState.ActiveProviderProfileId = + !SessionState.UsageCostHistoryExportShellState.ActiveProviderProfileId.IsEmpty() + ? SessionState.UsageCostHistoryExportShellState.ActiveProviderProfileId + : (!SessionState.SessionConfig.ProviderProfileDefinition.ProviderProfileId.IsEmpty() + ? SessionState.SessionConfig.ProviderProfileDefinition.ProviderProfileId + : TEXT("speech-provider/local-http-sidecar-profile-v1")); + ShellState.ActiveProviderDisplayLabel = + !SessionState.UsageCostHistoryExportShellState.ActiveProviderDisplayLabel.IsEmpty() + ? SessionState.UsageCostHistoryExportShellState.ActiveProviderDisplayLabel + : (!SessionState.SessionConfig.ProviderProfileDefinition.DisplayLabel.IsEmpty() + ? SessionState.SessionConfig.ProviderProfileDefinition.DisplayLabel + : TEXT("Local HTTP Speech Sidecar")); + ShellState.ActiveServiceLaneId = + !SessionState.UsageCostHistoryExportShellState.ActiveServiceLaneId.IsEmpty() + ? SessionState.UsageCostHistoryExportShellState.ActiveServiceLaneId + : (!SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId.IsEmpty() + ? SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId + : TEXT("speech/python-service")); + ShellState.SelectedChargeWindowId = + !SessionState.UsageCostHistoryExportShellState.SelectedHistoryWindowId.IsEmpty() + ? SessionState.UsageCostHistoryExportShellState.SelectedHistoryWindowId + : TEXT("current-session"); + ShellState.PostedChargeInspectionModeId = Profile.ReceiptSurfaceMode; + ShellState.bReceiptTimelineVisible = Profile.bSupportsReceiptTimeline; + ShellState.bPostedChargeSummaryVisible = Profile.bSupportsPostedChargeInspection; + ShellState.bVarianceReviewVisible = Profile.bSupportsVarianceReview; + ShellState.ReceiptEntries.Reset(); + ShellState.Issues.Reset(); + ShellState.ActiveIssue = FHyperTwistSpeechProviderReceiptReviewIssue(); + ShellState.ReceiptEntryCount = 0; + ShellState.ProviderPostedChargeCount = 0; + ShellState.EstimateOnlyEntryCount = 0; + ShellState.VarianceReviewEntryCount = 0; + ShellState.TotalEstimatedCostUsd = 0.0f; + ShellState.TotalReportedCostUsd = 0.0f; + ShellState.TotalAbsoluteVarianceUsd = 0.0f; + ShellState.HighestAbsoluteVarianceUsd = 0.0f; + ShellState.LatestReceiptEntryId.Reset(); + ShellState.bReceiptSummaryReady = false; + ShellState.bPostedChargeInspectionReady = false; + ShellState.bVarianceReviewReady = false; + + const FHyperTwistSpeechUsageCostHistoryExportShellState& HistoryState = + SessionState.UsageCostHistoryExportShellState; + if (!HistoryState.IsStructurallyValid() || HistoryState.HistoryEntries.Num() <= 0) + { + FHyperTwistSpeechProviderReceiptReviewIssue Issue; + Issue.IssueId = TEXT("provider-receipt-review-history-missing"); + Issue.IssueKind = TEXT("no-history"); + Issue.StatusLine = TEXT("Provider receipt review is waiting for usage/cost history."); + Issue.DetailLine = TEXT("Refresh the retained provider usage/cost history before receipt review begins."); + Issue.RecommendedActionId = Profile.RefreshActionId; + Issue.bRequiresOperatorAttention = false; + AddSpeechProviderReceiptReviewIssue(ShellState, Issue); + ShellState.StatusLine = Issue.StatusLine; + ShellState.DetailLine = Issue.DetailLine; + ShellState.ActiveIssueCount = ShellState.Issues.Num(); + return; + } + + const int32 ReceiptLimit = FMath::Max(1, Profile.RetainedReceiptEntryLimit); + const int32 StartIndex = FMath::Max(0, HistoryState.HistoryEntries.Num() - ReceiptLimit); + for (int32 EntryIndex = StartIndex; EntryIndex < HistoryState.HistoryEntries.Num(); ++EntryIndex) + { + const FHyperTwistSpeechUsageCostHistoryEntry& HistoryEntry = HistoryState.HistoryEntries[EntryIndex]; + if (!HistoryEntry.IsStructurallyValid()) + { + continue; + } + + const FHyperTwistSpeechProviderReceiptReviewEntry Entry = + BuildSpeechProviderReceiptReviewEntry(HistoryEntry, ShellState.SelectedChargeWindowId); + ShellState.ReceiptEntries.Add(Entry); + ShellState.LatestReceiptEntryId = Entry.ReceiptEntryId; + ShellState.LatestSourceKind = Entry.SourceKind; + ShellState.TotalEstimatedCostUsd += Entry.EstimatedCostUsd; + ShellState.TotalReportedCostUsd += Entry.ReportedCostUsd; + ShellState.TotalAbsoluteVarianceUsd += Entry.AbsoluteVarianceUsd; + ShellState.HighestAbsoluteVarianceUsd = + FMath::Max(ShellState.HighestAbsoluteVarianceUsd, Entry.AbsoluteVarianceUsd); + if (Entry.bHasProviderPostedCharge) + { + ++ShellState.ProviderPostedChargeCount; + } + if (Entry.bEstimateOnly) + { + ++ShellState.EstimateOnlyEntryCount; + } + if (Entry.bRequiresManualInspection) + { + ++ShellState.VarianceReviewEntryCount; + } + } + + ShellState.ReceiptEntryCount = ShellState.ReceiptEntries.Num(); + ShellState.bReceiptSummaryReady = ShellState.ReceiptEntryCount > 0; + ShellState.bPostedChargeInspectionReady = + Profile.bSupportsPostedChargeInspection && ShellState.ReceiptEntryCount > 0; + ShellState.bVarianceReviewReady = + Profile.bSupportsVarianceReview && ShellState.ReceiptEntryCount > 0; + + if (HistoryState.ActiveIssue.IsStructurallyValid() + && (HistoryState.ActiveIssue.IssueKind.Equals(TEXT("route-degraded"), ESearchCase::IgnoreCase) + || HistoryState.ActiveIssue.IssueKind.Equals(TEXT("hard-blocked"), ESearchCase::IgnoreCase) + || HistoryState.ActiveIssue.IssueKind.Equals(TEXT("rate-limited"), ESearchCase::IgnoreCase) + || HistoryState.ActiveIssue.IssueKind.Equals(TEXT("no-history"), ESearchCase::IgnoreCase))) + { + FHyperTwistSpeechProviderReceiptReviewIssue Issue; + Issue.IssueId = FString::Printf( + TEXT("provider-receipt-review-%s"), + *HistoryState.ActiveIssue.IssueKind + ); + Issue.IssueKind = HistoryState.ActiveIssue.IssueKind; + Issue.StatusLine = HistoryState.ActiveIssue.StatusLine; + Issue.DetailLine = HistoryState.ActiveIssue.DetailLine; + Issue.RecommendedActionId = HistoryState.ActiveIssue.RecommendedActionId; + Issue.bRequiresOperatorAttention = HistoryState.ActiveIssue.bRequiresOperatorAttention; + AddSpeechProviderReceiptReviewIssue(ShellState, Issue); + } + else if (ShellState.VarianceReviewEntryCount > 0) + { + FHyperTwistSpeechProviderReceiptReviewIssue Issue; + Issue.IssueId = TEXT("provider-receipt-review-variance-detected"); + Issue.IssueKind = TEXT("variance-detected"); + Issue.StatusLine = TEXT("Provider posted-charge variance requires review."); + Issue.DetailLine = TEXT("At least one posted provider charge diverges from the first-party estimate beyond the bounded review tolerance."); + Issue.RecommendedActionId = Profile.VarianceReviewActionId; + Issue.bRequiresOperatorAttention = true; + AddSpeechProviderReceiptReviewIssue(ShellState, Issue); + } + else if (ShellState.ProviderPostedChargeCount <= 0) + { + FHyperTwistSpeechProviderReceiptReviewIssue Issue; + Issue.IssueId = TEXT("provider-receipt-review-charge-pending"); + Issue.IssueKind = TEXT("provider-charge-pending"); + Issue.StatusLine = TEXT("Provider posted charges have not appeared yet."); + Issue.DetailLine = TEXT("Receipt review is bounded and ready, but the current retained snapshots still show estimate-only posture rather than a provider-posted charge."); + Issue.RecommendedActionId = Profile.PostedChargeInspectActionId; + Issue.bRequiresOperatorAttention = false; + AddSpeechProviderReceiptReviewIssue(ShellState, Issue); + } + + if (ShellState.ActiveIssue.IsStructurallyValid()) + { + ShellState.StatusLine = ShellState.ActiveIssue.StatusLine; + ShellState.DetailLine = ShellState.ActiveIssue.DetailLine; + } + else if (ShellState.ProviderPostedChargeCount > 0) + { + ShellState.StatusLine = TEXT("Posted provider charges are ready for inspection."); + ShellState.DetailLine = FString::Printf( + TEXT("%d posted charges are available for bounded comparison against first-party estimates for %s."), + ShellState.ProviderPostedChargeCount, + *ShellState.ActiveProviderDisplayLabel + ); + } + else + { + ShellState.StatusLine = TEXT("Provider receipt review is ready for bounded inspection."); + ShellState.DetailLine = FString::Printf( + TEXT("%d retained snapshots are available for %s, but provider-posted charges have not appeared yet."), + ShellState.ReceiptEntryCount, + *ShellState.ActiveProviderDisplayLabel + ); + } + + ShellState.ReceiptSummaryText = FString::Printf( + TEXT("providerProfileId=%s\nserviceLaneId=%s\nreceiptEntryCount=%d\nproviderPostedChargeCount=%d\n") + TEXT("estimateOnlyEntryCount=%d\nvarianceReviewEntryCount=%d\ntotalEstimatedCostUsd=%.2f\n") + TEXT("totalReportedCostUsd=%.2f\ntotalAbsoluteVarianceUsd=%.2f\nhighestAbsoluteVarianceUsd=%.2f\n") + TEXT("latestReceiptEntryId=%s\nlatestSourceKind=%s\nlastError=%s"), + *ShellState.ActiveProviderProfileId, + *ShellState.ActiveServiceLaneId, + ShellState.ReceiptEntryCount, + ShellState.ProviderPostedChargeCount, + ShellState.EstimateOnlyEntryCount, + ShellState.VarianceReviewEntryCount, + ShellState.TotalEstimatedCostUsd, + ShellState.TotalReportedCostUsd, + ShellState.TotalAbsoluteVarianceUsd, + ShellState.HighestAbsoluteVarianceUsd, + *ShellState.LatestReceiptEntryId, + *ShellState.LatestSourceKind, + *SessionState.LastError + ); + ShellState.ActiveIssueCount = ShellState.Issues.Num(); + } + + void SynchronizeSpeechProviderReceiptReviewState( + FHyperTwistTrainingCompanionSpeechSessionState& SessionState + ) + { + FHyperTwistSpeechSessionConfig EffectiveConfig = SessionState.SessionConfig; + ApplySpeechUsageCostDefaults(EffectiveConfig); + if (!SessionState.UsageCostHistoryExportShellState.IsStructurallyValid()) + { + SessionState.UsageCostHistoryExportShellState = + BuildSpeechUsageCostHistoryExportShellState(EffectiveConfig, &SessionState); + } + + const FHyperTwistSpeechProviderReceiptReviewShellProfile Profile = + EffectiveConfig.ProviderReceiptReviewShellProfileDefinition.IsStructurallyValid() + ? EffectiveConfig.ProviderReceiptReviewShellProfileDefinition + : BuildDefaultSpeechProviderReceiptReviewShellProfile( + !EffectiveConfig.ProviderReceiptReviewShellProfileId.IsEmpty() + ? EffectiveConfig.ProviderReceiptReviewShellProfileId + : TEXT("speech-provider-receipt-review-shell-v1") + ); + + if (!SessionState.ProviderReceiptReviewShellState.IsStructurallyValid()) + { + SessionState.ProviderReceiptReviewShellState = + BuildSpeechProviderReceiptReviewShellState(EffectiveConfig, &SessionState); + } + + FHyperTwistSpeechProviderReceiptReviewShellState& ShellState = + SessionState.ProviderReceiptReviewShellState; + ShellState.ReceiptReviewShellProfileId = Profile.ReceiptReviewShellProfileId; + ShellState.AvailableActionIds.Reset(); + for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings) + { + ShellState.AvailableActionIds.AddUnique(ActionBinding.ActionId); + } + + ApplySpeechProviderReceiptReviewPosture(Profile, SessionState, ShellState); + + ShellState.AvailableActionIds.AddUnique(Profile.RefreshActionId); + ShellState.AvailableActionIds.AddUnique(Profile.PostedChargeInspectActionId); + ShellState.AvailableActionIds.AddUnique(Profile.VarianceReviewActionId); + ShellState.AvailableActionIds.AddUnique(Profile.RouteInspectActionId); + ShellState.AvailableActionIds.AddUnique(Profile.BudgetInspectActionId); + if (ShellState.ActiveIssue.IsStructurallyValid()) + { + ShellState.AvailableActionIds.AddUnique(ShellState.ActiveIssue.RecommendedActionId); + } + } + FHyperTwistSpeechMicrophoneShellState BuildCoachCommandMicrophoneShellState( const FHyperTwistSpeechSessionConfig& SessionConfig, const FHyperTwistTrainingCompanionSpeechSessionState* PriorState, @@ -5247,6 +5673,55 @@ namespace HyperTwistTrainingSubsystemInternal return ShellState; } + FHyperTwistSpeechProviderReceiptReviewShellState BuildSpeechProviderReceiptReviewShellState( + const FHyperTwistSpeechSessionConfig& SessionConfig, + const FHyperTwistTrainingCompanionSpeechSessionState* PriorState + ) + { + const FHyperTwistSpeechProviderReceiptReviewShellProfile Profile = + SessionConfig.ProviderReceiptReviewShellProfileDefinition.IsStructurallyValid() + ? SessionConfig.ProviderReceiptReviewShellProfileDefinition + : BuildDefaultSpeechProviderReceiptReviewShellProfile( + !SessionConfig.ProviderReceiptReviewShellProfileId.IsEmpty() + ? SessionConfig.ProviderReceiptReviewShellProfileId + : TEXT("speech-provider-receipt-review-shell-v1") + ); + + FHyperTwistSpeechProviderReceiptReviewShellState ShellState; + ShellState.ReceiptReviewShellProfileId = Profile.ReceiptReviewShellProfileId; + ShellState.ActiveProviderProfileId = SessionConfig.ProviderProfileDefinition.IsStructurallyValid() + ? SessionConfig.ProviderProfileDefinition.ProviderProfileId + : TEXT("speech-provider/local-http-sidecar-profile-v1"); + ShellState.ActiveProviderDisplayLabel = SessionConfig.ProviderProfileDefinition.IsStructurallyValid() + ? SessionConfig.ProviderProfileDefinition.DisplayLabel + : TEXT("Local HTTP Speech Sidecar"); + ShellState.ActiveServiceLaneId = !SessionConfig.OrchestrationProfile.ServiceLaneId.IsEmpty() + ? SessionConfig.OrchestrationProfile.ServiceLaneId + : TEXT("speech/python-service"); + ShellState.SelectedChargeWindowId = !SessionConfig.CostEventTemplate.AggregationWindowId.IsEmpty() + ? SessionConfig.CostEventTemplate.AggregationWindowId + : TEXT("current-session"); + ShellState.PostedChargeInspectionModeId = Profile.ReceiptSurfaceMode; + ShellState.bReceiptTimelineVisible = Profile.bSupportsReceiptTimeline; + ShellState.bPostedChargeSummaryVisible = Profile.bSupportsPostedChargeInspection; + ShellState.bVarianceReviewVisible = Profile.bSupportsVarianceReview; + for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings) + { + ShellState.AvailableActionIds.AddUnique(ActionBinding.ActionId); + } + + if (PriorState != nullptr + && PriorState->ProviderReceiptReviewShellState.ReceiptReviewShellProfileId + == Profile.ReceiptReviewShellProfileId) + { + ShellState = PriorState->ProviderReceiptReviewShellState; + ShellState.ReceiptReviewShellProfileId = Profile.ReceiptReviewShellProfileId; + ShellState.PostedChargeInspectionModeId = Profile.ReceiptSurfaceMode; + } + + return ShellState; + } + FHyperTwistVisionCorrectionState BuildClassicCubeCorrectionState( const FHyperTwistVisionSessionConfig& SessionConfig, const FHyperTwistVisionReconstructionSession& ReconstructionSession, @@ -10621,6 +11096,11 @@ bool UHyperTwistTrainingSubsystem::OpenActiveCompanionSpeechSession(FString& Out SessionConfig, nullptr ); + ActiveCompanionSpeechSessionState.ProviderReceiptReviewShellState = + HyperTwistTrainingSubsystemInternal::BuildSpeechProviderReceiptReviewShellState( + SessionConfig, + nullptr + ); if (ActiveCompanionSpeechSessionState.bSessionOpen && ActiveCompanionSpeechSessionState.ActiveSessionId != SessionConfig.SessionId) @@ -10698,6 +11178,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostHistoryExportState( ActiveCompanionSpeechSessionState ); + HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderReceiptReviewState( + ActiveCompanionSpeechSessionState + ); return Result; } @@ -10875,6 +11358,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostHistoryExportState( ActiveCompanionSpeechSessionState ); + HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderReceiptReviewState( + ActiveCompanionSpeechSessionState + ); return Result; } @@ -11624,6 +12110,9 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth() HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostHistoryExportState( ActiveCompanionSpeechSessionState ); + HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderReceiptReviewState( + ActiveCompanionSpeechSessionState + ); return; } @@ -11655,6 +12144,9 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth() HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostHistoryExportState( ActiveCompanionSpeechSessionState ); + HyperTwistTrainingSubsystemInternal::SynchronizeSpeechProviderReceiptReviewState( + ActiveCompanionSpeechSessionState + ); } void UHyperTwistTrainingSubsystem::AppendRecognitionReplayEvent( diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h index 701d5ea..969e381 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h @@ -3256,6 +3256,387 @@ struct FHyperTwistSpeechUsageCostHistoryExportShellState } }; +USTRUCT(BlueprintType) +struct FHyperTwistSpeechProviderReceiptReviewShellProfile +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReceiptReviewShellProfileId = TEXT("speech-provider-receipt-review-shell-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ShellKind = TEXT("operator-provider-receipt-review"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReceiptSurfaceMode = TEXT("posted-charge-review"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString VarianceSurfaceMode = TEXT("estimate-versus-posted-charge"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RefreshActionId = TEXT("refresh-provider-receipt-review"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PostedChargeInspectActionId = TEXT("inspect-provider-posted-charges"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString VarianceReviewActionId = TEXT("review-provider-charge-variance"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RouteInspectActionId = TEXT("inspect-provider-route"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BudgetInspectActionId = TEXT("review-provider-budget"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RetainedReceiptEntryLimit = 12; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsReceiptTimeline = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsPostedChargeInspection = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsVarianceReview = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsEstimateOnlyFallback = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreservesFirstPartyReceiptInspectionOwnership = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Panels; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ActionBindings; + + bool IsStructurallyValid() const + { + if (ReceiptReviewShellProfileId.IsEmpty() + || ShellKind.IsEmpty() + || ReceiptSurfaceMode.IsEmpty() + || VarianceSurfaceMode.IsEmpty() + || RefreshActionId.IsEmpty() + || PostedChargeInspectActionId.IsEmpty() + || VarianceReviewActionId.IsEmpty() + || RouteInspectActionId.IsEmpty() + || BudgetInspectActionId.IsEmpty() + || RetainedReceiptEntryLimit <= 0 + || 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 FHyperTwistSpeechProviderReceiptReviewEntry +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReceiptEntryId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceKind = TEXT("history-derived-estimate-only"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString HistoryEntryId = TEXT("speech-history-entry/current-session"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UsageEventId = TEXT("speech-usage-event/current-session"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CostEventId = TEXT("speech-cost-event/current-session"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ServiceLaneId = TEXT("speech/python-service"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PricingSourceId = TEXT("first-party-estimate"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ChargeWindowId = TEXT("current-session"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CapturedAtUtc = TEXT("2026-05-25T00:00:00Z"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float EstimatedCostUsd = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float ReportedCostUsd = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float AbsoluteVarianceUsd = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString VarianceDirectionId = TEXT("no-variance"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasProviderPostedCharge = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bEstimateOnly = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bVarianceWithinTolerance = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRequiresManualInspection = false; + + bool IsStructurallyValid() const + { + return !ReceiptEntryId.IsEmpty() + && !SourceKind.IsEmpty() + && !HistoryEntryId.IsEmpty() + && !UsageEventId.IsEmpty() + && !CostEventId.IsEmpty() + && !ProviderProfileId.IsEmpty() + && !ServiceLaneId.IsEmpty() + && !PricingSourceId.IsEmpty() + && !ChargeWindowId.IsEmpty() + && !CapturedAtUtc.IsEmpty() + && EstimatedCostUsd >= 0.0f + && ReportedCostUsd >= 0.0f + && AbsoluteVarianceUsd >= 0.0f + && !VarianceDirectionId.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistSpeechProviderReceiptReviewIssue +{ + 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 bRequiresOperatorAttention = true; + + bool IsStructurallyValid() const + { + return !IssueId.IsEmpty() + && !IssueKind.IsEmpty() + && !StatusLine.IsEmpty() + && !DetailLine.IsEmpty() + && !RecommendedActionId.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistSpeechProviderReceiptReviewShellState +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReceiptReviewShellProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveProviderDisplayLabel = TEXT("Local HTTP Speech Sidecar"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveServiceLaneId = TEXT("speech/python-service"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedChargeWindowId = TEXT("current-session"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PostedChargeInspectionModeId = TEXT("posted-charge-review"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LatestReceiptEntryId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LatestSourceKind = TEXT("history-derived-estimate-only"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine = TEXT("Provider receipt review is ready for operator inspection."); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine = TEXT("First-party receipt review remains bounded above retained usage/cost history and does not widen into settlement."); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReceiptSummaryText; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ReceiptEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ProviderPostedChargeCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 EstimateOnlyEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 VarianceReviewEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float TotalEstimatedCostUsd = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float TotalReportedCostUsd = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float TotalAbsoluteVarianceUsd = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float HighestAbsoluteVarianceUsd = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bReceiptTimelineVisible = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPostedChargeSummaryVisible = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bVarianceReviewVisible = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bReceiptSummaryReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPostedChargeInspectionReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bVarianceReviewReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ActiveIssueCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistSpeechProviderReceiptReviewIssue ActiveIssue; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Issues; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ReceiptEntries; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray AvailableActionIds; + + bool IsStructurallyValid() const + { + if (ReceiptReviewShellProfileId.IsEmpty() + || ActiveProviderProfileId.IsEmpty() + || ActiveProviderDisplayLabel.IsEmpty() + || ActiveServiceLaneId.IsEmpty() + || SelectedChargeWindowId.IsEmpty() + || PostedChargeInspectionModeId.IsEmpty() + || LatestSourceKind.IsEmpty() + || StatusLine.IsEmpty() + || DetailLine.IsEmpty() + || ReceiptEntryCount < 0 + || ProviderPostedChargeCount < 0 + || EstimateOnlyEntryCount < 0 + || VarianceReviewEntryCount < 0 + || TotalEstimatedCostUsd < 0.0f + || TotalReportedCostUsd < 0.0f + || TotalAbsoluteVarianceUsd < 0.0f + || HighestAbsoluteVarianceUsd < 0.0f + || ActiveIssueCount < 0 + || AvailableActionIds.Num() <= 0) + { + return false; + } + + if (ReceiptEntryCount != ReceiptEntries.Num() + || ProviderPostedChargeCount > ReceiptEntryCount + || EstimateOnlyEntryCount > ReceiptEntryCount + || VarianceReviewEntryCount > ReceiptEntryCount) + { + return false; + } + + if (ReceiptEntryCount > 0 && LatestReceiptEntryId.IsEmpty()) + { + return false; + } + + if (bReceiptSummaryReady && ReceiptSummaryText.IsEmpty()) + { + return false; + } + + if (ActiveIssueCount != Issues.Num()) + { + return false; + } + + if (ActiveIssueCount > 0 && !ActiveIssue.IsStructurallyValid()) + { + return false; + } + + for (const FHyperTwistSpeechProviderReceiptReviewIssue& Issue : Issues) + { + if (!Issue.IsStructurallyValid()) + { + return false; + } + } + + for (const FHyperTwistSpeechProviderReceiptReviewEntry& Entry : ReceiptEntries) + { + if (!Entry.IsStructurallyValid()) + { + return false; + } + } + + for (const FString& ActionId : AvailableActionIds) + { + if (ActionId.IsEmpty()) + { + return false; + } + } + + return true; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistSpeechSessionConfig { @@ -3341,6 +3722,14 @@ struct FHyperTwistSpeechSessionConfig FHyperTwistSpeechUsageCostHistoryExportShellProfile UsageCostHistoryExportShellProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ProviderReceiptReviewShellProfileId = + TEXT("speech-provider-receipt-review-shell-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistSpeechProviderReceiptReviewShellProfile + ProviderReceiptReviewShellProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString PayloadCustodyProfileId = TEXT("downloadable-model-payload-custody-v1"); @@ -3391,6 +3780,8 @@ struct FHyperTwistSpeechSessionConfig || !UsageCostDashboardShellProfileDefinition.IsStructurallyValid() || UsageCostHistoryExportShellProfileId.IsEmpty() || !UsageCostHistoryExportShellProfileDefinition.IsStructurallyValid() + || ProviderReceiptReviewShellProfileId.IsEmpty() + || !ProviderReceiptReviewShellProfileDefinition.IsStructurallyValid() || PayloadCustodyProfileId.IsEmpty() || !PayloadCustodyProfileDefinition.IsStructurallyValid() || RequiredModelPayloads.Num() <= 0 diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 7ef66f1..4fcd2df 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -1946,6 +1946,9 @@ struct FHyperTwistTrainingCompanionSpeechSessionState UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistSpeechUsageCostHistoryExportShellState UsageCostHistoryExportShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistSpeechProviderReceiptReviewShellState ProviderReceiptReviewShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistSpeechTranscriptResult LastTranscriptResult; }; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistWhisperCppPhase6RACProviderReceiptReviewShellContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistWhisperCppPhase6RACProviderReceiptReviewShellContractTest.cpp new file mode 100644 index 0000000..9319c0a --- /dev/null +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistWhisperCppPhase6RACProviderReceiptReviewShellContractTest.cpp @@ -0,0 +1,285 @@ +// Copyright HyperTwist, Inc. All Rights Reserved. + +#include "Misc/AutomationTest.h" + +#include "Engine/GameInstance.h" +#include "HyperTwistRecognition/HyperTwistSpeechClient.h" +#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h" +#include "UObject/UnrealType.h" + +#if WITH_AUTOMATION_TESTS + +namespace HyperTwistWhisperCppPhase6RACTestInternal +{ + FHyperTwistTrainingDeck MakeSpeechDeck() + { + FHyperTwistTrainingDeck Deck; + Deck.DeckId = TEXT("phase6r-ac/provider-receipt-review"); + Deck.Title = TEXT("Phase 6R-AC Provider Receipt Review"); + Deck.DeliveryModes = { + EHyperTwistTrainingDeliveryMode::CoachReviewed + }; + + FHyperTwistTrainingCase TrainingCase; + TrainingCase.CaseId = TEXT("phase6r-ac-case"); + TrainingCase.PuzzleId = TEXT("cube/3x3x3"); + TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Sequence; + TrainingCase.PromptLabel = TEXT("Phase 6R-AC 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-ac-user"), + SessionId, + EHyperTwistTrainingDeliveryMode::CoachReviewed + ); + return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr; + } + + 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; + } + + FHyperTwistSpeechUtteranceEnvelope MakeUtterance() + { + FHyperTwistSpeechUtteranceEnvelope Utterance; + Utterance.AudioRef = TEXT("ready-coach"); + Utterance.SpeechStartMs = 120; + Utterance.SpeechEndMs = 840; + Utterance.SilenceGapMs = 240; + return Utterance; + } + + bool ContainsPanelId( + const TArray& Panels, + const FString& ExpectedPanelId + ) + { + for (const FHyperTwistSpeechShellPanelLayout& Panel : Panels) + { + if (Panel.PanelId == ExpectedPanelId) + { + return true; + } + } + + return false; + } + + bool ContainsActionId( + const TArray& ActionBindings, + const FString& ExpectedActionId + ) + { + for (const FHyperTwistSpeechShellActionBinding& ActionBinding : ActionBindings) + { + if (ActionBinding.ActionId == ExpectedActionId) + { + return true; + } + } + + return false; + } +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistWhisperCppPhase6RACProviderReceiptReviewSessionConfigTest, + "HyperTwist.Permissive.WhisperCpp.Phase6R.AC.ProviderReceiptReviewSessionConfig", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistWhisperCppPhase6RACProviderReceiptReviewSessionConfigTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistWhisperCppPhase6RACTestInternal::MakeSpeechSubsystem( + TEXT("phase6r-ac-config-session"), + TEXT("mock") + ); + TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AC."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + FString OpenError; + TestTrue(TEXT("The mock receipt-review route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError)); + TestTrue(TEXT("Opening the mock receipt-review speech session must not report an error."), OpenError.IsEmpty()); + + const FHyperTwistSpeechSessionConfig& Config = + TrainingSubsystem->GetActiveCompanionSpeechSessionState().SessionConfig; + + TestTrue(TEXT("The receipt-review session config must remain structurally valid."), Config.IsStructurallyValid()); + TestEqual(TEXT("The receipt-review shell profile id must be present."), Config.ProviderReceiptReviewShellProfileId, TEXT("speech-provider-receipt-review-shell-v1")); + TestTrue(TEXT("The receipt-review shell profile must remain structurally valid."), Config.ProviderReceiptReviewShellProfileDefinition.IsStructurallyValid()); + TestEqual(TEXT("The receipt-review shell must preserve the operator-facing shell kind."), Config.ProviderReceiptReviewShellProfileDefinition.ShellKind, TEXT("operator-provider-receipt-review")); + TestEqual(TEXT("The receipt-review shell must preserve the posted-charge inspect action id."), Config.ProviderReceiptReviewShellProfileDefinition.PostedChargeInspectActionId, TEXT("inspect-provider-posted-charges")); + TestEqual(TEXT("The receipt-review shell must preserve the variance review action id."), Config.ProviderReceiptReviewShellProfileDefinition.VarianceReviewActionId, TEXT("review-provider-charge-variance")); + TestTrue(TEXT("The receipt-review shell must include the receipt timeline panel."), HyperTwistWhisperCppPhase6RACTestInternal::ContainsPanelId(Config.ProviderReceiptReviewShellProfileDefinition.Panels, TEXT("receipt-timeline-card"))); + TestTrue(TEXT("The receipt-review shell must include the posted-charge inspection action."), HyperTwistWhisperCppPhase6RACTestInternal::ContainsActionId(Config.ProviderReceiptReviewShellProfileDefinition.ActionBindings, TEXT("inspect-provider-posted-charges"))); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistWhisperCppPhase6RACProviderReceiptReviewShellStateTest, + "HyperTwist.Permissive.WhisperCpp.Phase6R.AC.ProviderReceiptReviewShellState", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistWhisperCppPhase6RACProviderReceiptReviewShellStateTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistWhisperCppPhase6RACTestInternal::MakeSpeechSubsystem( + TEXT("phase6r-ac-shell-session"), + TEXT("mock") + ); + TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AC."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + FString OpenError; + TestTrue(TEXT("The mock receipt-review route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError)); + TestTrue(TEXT("Opening the mock receipt-review speech session must not report an error."), OpenError.IsEmpty()); + + const FHyperTwistSpeechTranscriptResult Result = + TrainingSubsystem->SubmitActiveCompanionSpeechUtterance( + HyperTwistWhisperCppPhase6RACTestInternal::MakeUtterance() + ); + TestTrue(TEXT("The mock receipt-review utterance must produce a final transcript."), Result.bIsFinal); + TestTrue(TEXT("The mock receipt-review utterance must remain structurally valid."), Result.IsStructurallyValid()); + + const FHyperTwistTrainingCompanionSpeechSessionState SessionState = + TrainingSubsystem->GetActiveCompanionSpeechSessionState(); + const FHyperTwistSpeechServiceHealth& Health = SessionState.ServiceHealth; + const FHyperTwistSpeechProviderReceiptReviewShellState& ReceiptReview = + SessionState.ProviderReceiptReviewShellState; + + TestTrue(TEXT("The service health must expose the receipt-review shell."), Health.Capabilities.Contains(TEXT("providerReceiptReviewShell"))); + TestTrue(TEXT("The service health must expose posted-charge inspection."), Health.Capabilities.Contains(TEXT("providerPostedChargeInspection"))); + TestTrue(TEXT("The service health must expose charge-variance review."), Health.Capabilities.Contains(TEXT("providerChargeVarianceReview"))); + + TestTrue(TEXT("The receipt-review shell state must remain structurally valid."), ReceiptReview.IsStructurallyValid()); + TestEqual(TEXT("The receipt-review shell must preserve the mock provider profile."), ReceiptReview.ActiveProviderProfileId, TEXT("speech-provider/mock-offline-profile-v1")); + TestEqual(TEXT("The receipt-review shell must retain exactly two bounded receipt entries after open and submit."), ReceiptReview.ReceiptEntryCount, 2); + TestEqual(TEXT("The receipt-review shell must preserve the latest source kind."), ReceiptReview.LatestSourceKind, TEXT("history-derived-estimate-only")); + TestTrue(TEXT("The receipt-review shell must preserve summary readiness."), ReceiptReview.bReceiptSummaryReady); + TestTrue(TEXT("The receipt-review shell must preserve posted-charge inspection readiness."), ReceiptReview.bPostedChargeInspectionReady); + TestTrue(TEXT("The receipt-review shell must preserve variance-review readiness."), ReceiptReview.bVarianceReviewReady); + TestTrue(TEXT("The receipt-review shell must preserve the posted-charge inspection action."), ReceiptReview.AvailableActionIds.Contains(TEXT("inspect-provider-posted-charges"))); + TestTrue(TEXT("The receipt-review shell must preserve the variance-review action."), ReceiptReview.AvailableActionIds.Contains(TEXT("review-provider-charge-variance"))); + TestEqual(TEXT("The receipt-review shell must preserve zero provider-posted charges in the mock route."), ReceiptReview.ProviderPostedChargeCount, 0); + TestEqual(TEXT("The receipt-review shell must preserve the estimate-only entry count."), ReceiptReview.EstimateOnlyEntryCount, 2); + TestTrue(TEXT("The receipt-review shell must accumulate the mock estimated cost."), FMath::IsNearlyEqual(ReceiptReview.TotalEstimatedCostUsd, 0.02f)); + TestTrue(TEXT("The receipt-review shell must preserve zero variance while no provider charge is posted."), FMath::IsNearlyEqual(ReceiptReview.TotalAbsoluteVarianceUsd, 0.0f)); + TestEqual(TEXT("The receipt-review shell must surface the pending provider-charge issue."), ReceiptReview.ActiveIssue.IssueKind, TEXT("provider-charge-pending")); + TestTrue(TEXT("The receipt-review summary text must preserve the bounded posted-charge count."), ReceiptReview.ReceiptSummaryText.Contains(TEXT("providerPostedChargeCount=0"))); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistWhisperCppPhase6RACProviderBackedReceiptReviewFallbackTest, + "HyperTwist.Permissive.WhisperCpp.Phase6R.AC.ProviderBackedReceiptReviewFallback", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistWhisperCppPhase6RACProviderBackedReceiptReviewFallbackTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistWhisperCppPhase6RACTestInternal::MakeSpeechSubsystem( + TEXT("phase6r-ac-provider-session"), + TEXT("http-sidecar") + ); + TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AC."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + UHyperTwistHttpSpeechClient* HttpSpeechClient = + HyperTwistWhisperCppPhase6RACTestInternal::ResolveHttpSpeechClient(TrainingSubsystem); + TestNotNull(TEXT("The provider-backed speech client must be available for the receipt-review fallback test."), HttpSpeechClient); + if (HttpSpeechClient == nullptr) + { + return false; + } + + HttpSpeechClient->ServiceBaseUrl.Reset(); + + FString OpenError; + TestFalse(TEXT("The provider-backed receipt-review path must fail cleanly when no provider endpoint is configured."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError)); + TestEqual(TEXT("The provider-backed receipt-review path must report the missing service base URL."), OpenError, TEXT("service-base-url-missing")); + + const FHyperTwistTrainingCompanionSpeechSessionState SessionState = + TrainingSubsystem->GetActiveCompanionSpeechSessionState(); + const FHyperTwistSpeechProviderReceiptReviewShellState& ReceiptReview = + SessionState.ProviderReceiptReviewShellState; + + TestTrue(TEXT("The provider-backed receipt-review fallback must remain structurally valid."), ReceiptReview.IsStructurallyValid()); + TestEqual(TEXT("The fallback receipt-review shell must preserve the sidecar profile id."), ReceiptReview.ActiveProviderProfileId, TEXT("speech-provider/local-http-sidecar-profile-v1")); + TestEqual(TEXT("The fallback receipt-review shell must preserve the fallback source kind."), ReceiptReview.LatestSourceKind, TEXT("provider-fallback-health")); + TestEqual(TEXT("The fallback receipt-review shell must preserve one receipt entry."), ReceiptReview.ReceiptEntryCount, 1); + TestEqual(TEXT("The fallback receipt-review shell must surface one active issue."), ReceiptReview.ActiveIssueCount, 1); + TestEqual(TEXT("The fallback receipt-review shell must surface the route-degraded issue kind."), ReceiptReview.ActiveIssue.IssueKind, TEXT("route-degraded")); + TestTrue(TEXT("The fallback receipt-review shell must still expose summary readiness for bounded inspection output."), ReceiptReview.bReceiptSummaryReady); + TestTrue(TEXT("The fallback receipt-review summary text must preserve the missing-endpoint reason."), ReceiptReview.ReceiptSummaryText.Contains(TEXT("service-base-url-missing"))); + return true; +} + +#endif diff --git a/docs/REPO_LICENSE_TRACKING.md b/docs/REPO_LICENSE_TRACKING.md index 0e85f9b..ee95ba4 100644 --- a/docs/REPO_LICENSE_TRACKING.md +++ b/docs/REPO_LICENSE_TRACKING.md @@ -199,8 +199,12 @@ Status update on `2026-05-21`: control pass is now consumed - the bounded first-party `Phase 6R-AB` provider usage/cost history/export shell packet is now landed in current code -- the current next bounded move is a source-backed `Phase 6R-AC` first-party provider receipt - review and posted-charge inspection preparation/control pass, not a new restrictive packet by +- the generic source-backed `Phase 6R-AC` first-party provider receipt review and posted-charge + inspection control pass is now consumed +- the bounded first-party `Phase 6R-AC` provider receipt review and posted-charge inspection + packet is now landed in current code +- the current next bounded move is a source-backed `Phase 6R-AD` first-party provider billing + settlement and invoice reconciliation preparation/control pass, not a new restrictive packet by default - use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A` ownership contract for the current queue after that correction @@ -350,8 +354,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-AC` first-party provider receipt review and - posted-charge inspection preparation/control pass +- the next bounded move is a source-backed `Phase 6R-AD` first-party provider billing settlement + and invoice reconciliation preparation/control pass Companion docs: diff --git a/docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_IMPLEMENTATION_PACKET_2026-05-25.md b/docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_IMPLEMENTATION_PACKET_2026-05-25.md new file mode 100644 index 0000000..e62f773 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_IMPLEMENTATION_PACKET_2026-05-25.md @@ -0,0 +1,168 @@ +# HyperTwist Phase 6R-AC first-party provider receipt review and posted-charge inspection implementation packet + +Created on `2026-05-25` + +## Status + +- first-party HyperTwist packet +- bounded `Phase 6R-AC` implementation slice + +## Purpose + +This packet lands the next bounded first-party slice above the landed speech +session, microphone-shell, payload-custody, provider-custody, +provider-routing, normalized usage/cost event, operator-facing +usage/cost dashboard, and provider usage/cost history/export seams. + +The landed slice is: + +- first-party provider receipt review and posted-charge inspection shell + +It is not: + +- an actual billing settlement packet +- an invoice reconciliation packet +- an actual payment execution packet +- a provider-portal ownership packet +- an actual payload-shipping packet +- a real device-permission workflow packet +- a native audio-device route ownership 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_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_PREPARATION_PACKET_2026-05-25.md` +- `docs/arch/HYPERTWIST_PHASE6R_AB_FIRST_PARTY_PROVIDER_USAGE_COST_HISTORY_EXPORT_SHELL_IMPLEMENTATION_PACKET_2026-05-25.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 receipt-review shell owner +- `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 receipt-review seam through: + +- retained recognition contract types for: + - `FHyperTwistSpeechProviderReceiptReviewShellProfile` + - `FHyperTwistSpeechProviderReceiptReviewEntry` + - `FHyperTwistSpeechProviderReceiptReviewIssue` + - `FHyperTwistSpeechProviderReceiptReviewShellState` + - expanded `FHyperTwistSpeechSessionConfig` + - expanded `FHyperTwistTrainingCompanionSpeechSessionState` +- contract-library defaults for: + - receipt-review shell profile ids and definitions + - receipt-review panel ids and action bindings + - service-health capability exposure +- active companion session-config, service-health, and derived receipt-review + shell composition in: + - `UHyperTwistTrainingSubsystem` +- direct speech-client health capability exposure in: + - `UHyperTwistHttpSpeechClient` + - `UHyperTwistMockSpeechClient` +- focused automation coverage in: + - `HyperTwistWhisperCppPhase6RACProviderReceiptReviewShellContractTest.cpp` + +## Why this is still intentionally bounded + +This packet lands the receipt-review and posted-charge inspection shell family +only. + +Still deferred: + +- provider billing settlement and invoice reconciliation shell surfaces +- actual payment execution or provider-portal ownership +- actual downloadable model/payload shipping +- real device-permission workflow +- native audio-device route ownership beyond bounded shell posture +- broad assistant-platform scope + +## Validation + +Build validation: + +- `UnrealHyperTwistEditor Win64 Development` + +Focused automation validation: + +- `HyperTwist.Permissive.WhisperCpp.Phase6R.AC` `3/3` + +Regression automation validation: + +- `HyperTwist.Permissive.WhisperCpp.Phase6R.AB` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.AA` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.Z` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.Y` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.X` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.W` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.V` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.U` `3/3` +- `HyperTwist.Permissive.WhisperCpp.Phase6R.E` `3/3` +- `HyperTwist.Permissive.FasterWhisper.Phase6R.F` `3/3` +- `HyperTwist.Permissive.Piper.Phase6R.G` `3/3` +- `HyperTwist.Permissive.Coqui.Phase6R.H` `3/3` +- `HyperTwist.CleanRoom.CubeDesk` `14/14` + +Non-blocking warnings stayed limited to the existing Unreal headless/editor +noise, the pre-existing plugin dependency warning on `UnrealMCP`, and the +known `http:/speech/health` and `http:/vision/health` hostname-resolution +warnings in provider-backed automation. + +## Queue effect + +This packet consumes the current `Phase 6R-AC` 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 + - 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 for: + - receipt-review shell profile ids and definitions + - derived receipt-review entry and issue/state posture + - companion-session receipt-review composition + - capability exposure for: + - `providerReceiptReviewShell` + - `providerPostedChargeInspection` + - `providerChargeVarianceReview` +- still deferred: + - provider billing settlement and invoice reconciliation shell surfaces + - actual payment execution or provider-portal ownership + - actual downloadable model/payload shipping + - real device-permission workflow + - native audio-device route ownership beyond bounded shell posture + - broad assistant-platform scope + +The next clean move is: + +- a source-backed `Phase 6R-AD` first-party provider billing settlement and + invoice reconciliation preparation/control pass + +Keep the future sequencing guards visible: + +- first-party HyperTwist + - keep the top-level provider/session contract, routing/policy lane, + usage/cost governance lane, dashboard shell lane, history/export shell + lane, and receipt-review shell lane first-party +- `ggml-org/whisper.cpp` + - keep code-license judgments separate from actual model or payload shipping + review in any follow-on packet +- `SYSTRAN/faster-whisper` + - keep the row complementary to the landed provider/session, custody, + routing, usage/cost, dashboard, history/export, and receipt-review seams + rather than widening it into settlement or billing ownership diff --git a/docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_PREPARATION_PACKET_2026-05-25.md b/docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_PREPARATION_PACKET_2026-05-25.md new file mode 100644 index 0000000..3f4823e --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_AC_FIRST_PARTY_PROVIDER_RECEIPT_REVIEW_AND_POSTED_CHARGE_INSPECTION_PREPARATION_PACKET_2026-05-25.md @@ -0,0 +1,135 @@ +# HyperTwist Phase 6R-AC first-party provider receipt review and posted-charge inspection preparation packet + +Created on `2026-05-25` + +## Status + +- first-party HyperTwist packet +- source-backed `Phase 6R-AC` preparation/control slice + +## Purpose + +This packet scopes the next bounded first-party slice above the landed speech +session, microphone-shell, payload-custody, provider-custody, +provider-routing, normalized usage/cost event, operator-facing +usage/cost dashboard, and provider usage/cost history/export seams. + +The granted slice is: + +- first-party provider receipt review and posted-charge inspection shell only + +It is not: + +- an actual billing settlement packet +- an invoice reconciliation packet +- an actual payment execution packet +- a provider-portal ownership packet +- an actual payload-shipping packet +- a real device-permission workflow packet +- a native audio-device route ownership 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_AB_FIRST_PARTY_PROVIDER_USAGE_COST_HISTORY_EXPORT_SHELL_IMPLEMENTATION_PACKET_2026-05-25.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 receipt-review shell owner +- `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-AC` slice may land: + +1. first-party receipt-review shell profile ids and definitions +2. derived receipt-review entry, issue, and shell-state record shapes +3. session-config and companion-session-state expansion for those shapes +4. bounded capability exposure for: + - `providerReceiptReviewShell` + - `providerPostedChargeInspection` + - `providerChargeVarianceReview` +5. focused automation for estimate-only review posture and provider-backed + fallback inspection posture + +The packet must stay out of: + +- actual billing settlement +- invoice reconciliation ownership +- actual payment execution +- provider-portal ownership +- actual payload download or redistribution ownership +- real device-permission workflow ownership +- native audio-device route ownership + +## Proposed implementation shape + +Land the narrower first-party boundary through: + +- retained recognition contract types for: + - provider receipt-review shell profile + - provider receipt-review entry + - provider receipt-review issue + - provider receipt-review shell state + - expanded speech session config + - expanded companion speech session state +- contract-library defaults for: + - receipt-review shell profile ids and definitions + - receipt-review action bindings and panel ids + - service-health capability exposure +- training-subsystem composition for: + - bounded review posture derived from retained history/export snapshots + - posted-charge inspection readiness posture + - charge-variance review posture + - route-degraded and charge-pending issue posture +- direct speech-client health capability exposure for: + - `providerReceiptReviewShell` + - `providerPostedChargeInspection` + - `providerChargeVarianceReview` + +## Validation target + +Validate with: + +- Unreal build for `UnrealHyperTwistEditor Win64 Development` +- focused automation: + - `HyperTwist.Permissive.WhisperCpp.Phase6R.AC` +- regressions: + - `HyperTwist.Permissive.WhisperCpp.Phase6R.AB` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.AA` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.Z` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.Y` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.X` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.W` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.V` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.U` + - `HyperTwist.Permissive.WhisperCpp.Phase6R.E` + - `HyperTwist.Permissive.FasterWhisper.Phase6R.F` + - `HyperTwist.Permissive.Piper.Phase6R.G` + - `HyperTwist.Permissive.Coqui.Phase6R.H` + - `HyperTwist.CleanRoom.CubeDesk` + +## Queue effect + +If this packet lands cleanly, the next bounded move becomes: + +- a source-backed `Phase 6R-AD` first-party provider billing settlement and + invoice reconciliation preparation/control pass + +Keep the narrow sequencing guards visible: + +- keep actual billing settlement and invoice reconciliation separate from + receipt-review shell ownership +- keep actual payment execution or provider-portal ownership separate from + receipt-review shell ownership +- keep actual payload shipping separate from usage/cost governance +- keep real device-permission workflow and native capture-route ownership + outside this packet diff --git a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md index abe7364..0a26672 100644 --- a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md +++ b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md @@ -176,15 +176,19 @@ The next bounded move is now: shell control pass is now consumed 30. the bounded first-party `Phase 6R-AB` provider usage/cost history/export shell packet is now landed in current code -31. the next bounded move is a source-backed `Phase 6R-AC` first-party provider receipt review - and posted-charge inspection preparation/control pass -32. keep the speech-lane guard visible: +31. the generic source-backed `Phase 6R-AC` first-party provider receipt review and + posted-charge inspection control pass is now consumed +32. the bounded first-party `Phase 6R-AC` provider receipt review and posted-charge inspection + packet is now landed in current code +33. the next bounded move is a source-backed `Phase 6R-AD` first-party provider billing + settlement and invoice reconciliation preparation/control pass +34. keep the speech-lane guard visible: - keep code-license judgments separate from model, voice, and payload-license review -33. keep the provider-neutral speech-lane guard visible: +35. 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 -34. keep the `MagicTile` guard visible: +36. 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/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md b/docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md index d5d2099..2678f78 100644 --- a/docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md +++ b/docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md @@ -218,9 +218,12 @@ Current landed ordering status: - steps `1` through `5` now have bounded first-party implementation anchors - operator-facing usage/cost history/export shells now also have a bounded first-party implementation anchor -- provider receipt review and posted-charge inspection remain the next bounded - first-party gap above the landed history/export shell -- actual billing/settlement remains later first-party work +- provider receipt review and posted-charge inspection now also have a bounded + first-party implementation anchor +- provider billing settlement and invoice reconciliation remain the next + bounded first-party gap above the landed receipt-review shell +- actual payment execution or provider-portal ownership remains later + first-party work - provider-specific adapters remain later work Do not invert that order. 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 3fc4971..d792f21 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md @@ -128,6 +128,7 @@ Do not collapse those three tiers into one undifferentiated "features" voice. | First-party normalized usage/cost event surface | Implemented now | landed first-party `Phase 6R-Z` | First-party accounting-profile definitions, normalized usage/cost event templates and health summaries, quota/rate-limit state, and bounded capability exposure are now live above the landed provider-routing seam. | | First-party operator-facing provider usage/cost dashboard shell | Implemented now | landed first-party `Phase 6R-AA` | First-party dashboard shell profiles, dashboard issue/state objects, quota escalation banners, route diagnostics, and operator-facing panel/action contracts are now live above the landed normalized usage/cost seam. | | First-party provider usage/cost history/export shell | Implemented now | landed first-party `Phase 6R-AB` | First-party rolling history entries, export issue/state objects, summary export preview/copy posture, and operator-facing panel/action contracts are now live above the landed provider usage/cost dashboard seam. | +| 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. | | 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. | @@ -225,6 +226,7 @@ repo. | First-party normalized usage/cost event surface | Implemented now | landed first-party `Phase 6R-Z` packet | Current bounded accounting-profile ids/definitions, normalized usage/cost event summaries, quota/rate-limit state, and bounded capability exposure above the landed provider-routing seam. | | First-party operator-facing provider usage/cost dashboard shell | Implemented now | landed first-party `Phase 6R-AA` packet | Current bounded dashboard shell profile ids/definitions, dashboard issue/state posture, and capability exposure above the landed normalized usage/cost seam. | | First-party provider usage/cost history/export shell | Implemented now | landed first-party `Phase 6R-AB` packet | Current bounded history/export shell profile ids/definitions, rolling history entry and export issue/state posture, summary export preview/copy behavior, and capability exposure above the landed provider usage/cost dashboard seam. | +| First-party provider receipt review and posted-charge inspection shell | Implemented now | landed first-party `Phase 6R-AC` packet | Current bounded receipt-review shell profile ids/definitions, derived receipt-review entry and issue/state posture, and capability exposure above the landed provider usage/cost history/export seam. | | OpenAI-compatible custom endpoint support | Deep-source grounded retained | doctrine-defined first-party target | First-class target is now represented in the landed custody model, but full routing/runtime support is still deferred. | | Provider-specific overlays | Shallow placeholder | future provider-family work only | Keep internal until source-grounded and normalized. | diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md index e9aabe0..09e1a26 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -175,10 +175,14 @@ Canonical discovery surfaces for roadmap interpretation: control pass is now consumed - the bounded first-party `Phase 6R-AB` provider usage/cost history/export shell packet is now landed in current code -- the current next bounded move is a source-backed `Phase 6R-AC` first-party provider receipt - review and posted-charge inspection preparation/control pass, while keeping actual billing, - settlement, real device-permission workflow, native capture-route ownership, and actual - payload shipping separately deferred +- the generic source-backed `Phase 6R-AC` first-party provider receipt review and posted-charge + inspection control pass is now consumed +- the bounded first-party `Phase 6R-AC` provider receipt review and posted-charge inspection + packet is now landed in current code +- the current next bounded move is a source-backed `Phase 6R-AD` first-party provider billing + settlement and invoice reconciliation preparation/control pass, while keeping actual payment + execution, real device-permission workflow, native capture-route 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 @@ -265,14 +269,14 @@ 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-AC` first-party provider -receipt review and posted-charge inspection preparation/control pass. +The next bounded move is a source-backed `Phase 6R-AD` first-party provider +billing settlement and invoice reconciliation preparation/control pass. Queue interpretation after that control pass: - then continue with the retained repo-row queue - highest current repo-row queue pressure sits in: - - the first-party provider receipt review and posted-charge inspection shell + - the first-party provider billing settlement and invoice reconciliation remainder above the partially landed `whisper.cpp` and `faster-whisper` speech seams - `HactarCE/Hyperspeedcube` is now closed for the currently justified retained row: @@ -365,6 +369,8 @@ Queue interpretation after that control pass: - first-party provider routing and policy boundary - first-party normalized usage/cost event-model boundary - first-party provider usage/cost history/export shell boundary + - first-party provider receipt review and posted-charge inspection shell + boundary - bounded shell capability exposure for: - microphone capture shell - device-permission shell @@ -382,9 +388,12 @@ Queue interpretation after that control pass: - provider usage/cost history shell - provider usage/cost export preview - provider usage/cost summary copy + - provider receipt review shell + - provider posted-charge inspection + - provider charge-variance review - still deferred: - - provider receipt review and posted-charge inspection shell surfaces - - actual billing settlement or invoice reconciliation + - provider billing settlement and invoice reconciliation shell surfaces + - actual payment execution or provider-portal ownership - real device-permission workflow - native capture-route ownership beyond bounded shell posture - actual downloadable model or payload shipping @@ -432,14 +441,14 @@ Queue interpretation after that control pass: - viseme / gesture runtime integration - broad assistant-platform scope - the next queue shape is now: - - first-party provider receipt review and posted-charge inspection shell + - first-party provider billing settlement and invoice reconciliation shell assessment above the landed speech session, shell, payload-custody, provider-custody, provider-routing, normalized usage/cost, operator-facing - dashboard, and history/export seams + dashboard, history/export, and receipt-review seams - the next bounded move should stay narrow: - - a source-backed `Phase 6R-AC` control pass before any widening into actual - billing settlement, invoice reconciliation, actual payload shipping, real - device-permission workflow, broad voice-output ownership, or broad + - a source-backed `Phase 6R-AD` control pass before any widening into actual + payment execution, provider-portal ownership, actual payload shipping, + real device-permission workflow, 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