diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp index 4fd06cc..7dba5ab 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -146,6 +146,47 @@ namespace HyperTwistContractLibraryInternal return UpdatedState; } + FString ResolveSampleRepositoryUserId(const FHyperTwistTrainingRepositoryState& RepositoryState) + { + if (RepositoryState.RunRecords.Num() > 0 && + !RepositoryState.RunRecords[0].Session.UserId.IsEmpty()) + { + return RepositoryState.RunRecords[0].Session.UserId; + } + + if (RepositoryState.LearnerStates.Num() > 0 && + !RepositoryState.LearnerStates[0].UserId.IsEmpty()) + { + return RepositoryState.LearnerStates[0].UserId; + } + + if (RepositoryState.ReviewPlans.Num() > 0 && + !RepositoryState.ReviewPlans[0].UserId.IsEmpty()) + { + return RepositoryState.ReviewPlans[0].UserId; + } + + if (RepositoryState.StoredSessionTemplates.Num() > 0 && + !RepositoryState.StoredSessionTemplates[0].UserId.IsEmpty()) + { + return RepositoryState.StoredSessionTemplates[0].UserId; + } + + if (RepositoryState.StoredCoachActionPlans.Num() > 0 && + !RepositoryState.StoredCoachActionPlans[0].UserId.IsEmpty()) + { + return RepositoryState.StoredCoachActionPlans[0].UserId; + } + + if (RepositoryState.StoredCoachSessionQueues.Num() > 0 && + !RepositoryState.StoredCoachSessionQueues[0].UserId.IsEmpty()) + { + return RepositoryState.StoredCoachSessionQueues[0].UserId; + } + + return FString(); + } + FHyperTwistTrainingRepositoryState BuildSampleTrainingRepositoryStateBase() { const FHyperTwistTrainingRunStepResult StepResult = @@ -3691,34 +3732,8 @@ FHyperTwistMemoryLedgerState UHyperTwistContractLibrary::MakeSampleTrainingMemor FHyperTwistTrainingRepositoryState RepositoryState = MakeSampleTrainingRepositoryState(); RepositoryState = HyperTwistContractLibraryInternal::AppendSampleCoachArtifacts(RepositoryState); - FString UserId; - if (RepositoryState.RunRecords.Num() > 0 && !RepositoryState.RunRecords[0].Session.UserId.IsEmpty()) - { - UserId = RepositoryState.RunRecords[0].Session.UserId; - } - else if (RepositoryState.LearnerStates.Num() > 0 && !RepositoryState.LearnerStates[0].UserId.IsEmpty()) - { - UserId = RepositoryState.LearnerStates[0].UserId; - } - else if (RepositoryState.ReviewPlans.Num() > 0 && !RepositoryState.ReviewPlans[0].UserId.IsEmpty()) - { - UserId = RepositoryState.ReviewPlans[0].UserId; - } - else if (RepositoryState.StoredSessionTemplates.Num() > 0 && - !RepositoryState.StoredSessionTemplates[0].UserId.IsEmpty()) - { - UserId = RepositoryState.StoredSessionTemplates[0].UserId; - } - else if (RepositoryState.StoredCoachActionPlans.Num() > 0 && - !RepositoryState.StoredCoachActionPlans[0].UserId.IsEmpty()) - { - UserId = RepositoryState.StoredCoachActionPlans[0].UserId; - } - else if (RepositoryState.StoredCoachSessionQueues.Num() > 0 && - !RepositoryState.StoredCoachSessionQueues[0].UserId.IsEmpty()) - { - UserId = RepositoryState.StoredCoachSessionQueues[0].UserId; - } + const FString UserId = + HyperTwistContractLibraryInternal::ResolveSampleRepositoryUserId(RepositoryState); if (UserId.IsEmpty()) { @@ -3732,6 +3747,26 @@ FHyperTwistMemoryLedgerState UHyperTwistContractLibrary::MakeSampleTrainingMemor ); } +FHyperTwistMemoryChronicleContinuityState + UHyperTwistContractLibrary::MakeSampleTrainingMemoryChronicleContinuityState() +{ + FHyperTwistTrainingRepositoryState RepositoryState = MakeSampleTrainingRepositoryState(); + RepositoryState = HyperTwistContractLibraryInternal::AppendSampleCoachArtifacts(RepositoryState); + + const FString UserId = + HyperTwistContractLibraryInternal::ResolveSampleRepositoryUserId(RepositoryState); + if (UserId.IsEmpty()) + { + return FHyperTwistMemoryChronicleContinuityState(); + } + + return UHyperTwistMemoryCoreLibrary::DeriveMemoryChronicleContinuityState( + RepositoryState, + UserId, + TEXT("2026-04-28T12:40:00Z") + ); +} + FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistContractLibrary::MakeSampleTrainingRepositoryIntegrityReport() { FHyperTwistTrainingRepositoryState RepositoryState = MakeSampleTrainingRepositoryState(); @@ -4798,6 +4833,13 @@ FString UHyperTwistContractLibrary::SerializeMemoryLedgerStateToJson( return HyperTwistContractLibraryInternal::SerializeStructToJson(MemoryLedgerState); } +FString UHyperTwistContractLibrary::SerializeMemoryChronicleContinuityStateToJson( + const FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState +) +{ + return HyperTwistContractLibraryInternal::SerializeStructToJson(ChronicleContinuityState); +} + FString UHyperTwistContractLibrary::SerializeTrainingTimerExportPacketToJson( const FHyperTwistTrainingTimerExportPacket& TimerExportPacket ) @@ -4865,6 +4907,17 @@ bool UHyperTwistContractLibrary::DeserializeMemoryLedgerStateFromJson( return HyperTwistContractLibraryInternal::DeserializeStructFromJson(Json, OutMemoryLedgerState); } +bool UHyperTwistContractLibrary::DeserializeMemoryChronicleContinuityStateFromJson( + const FString& Json, + FHyperTwistMemoryChronicleContinuityState& OutChronicleContinuityState +) +{ + return HyperTwistContractLibraryInternal::DeserializeStructFromJson( + Json, + OutChronicleContinuityState + ); +} + bool UHyperTwistContractLibrary::DeserializeTrainingTimerExportPacketFromJson( const FString& Json, FHyperTwistTrainingTimerExportPacket& OutTimerExportPacket diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistMemory/HyperTwistMemoryCoreLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistMemory/HyperTwistMemoryCoreLibrary.cpp index 2e16c09..b167f70 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistMemory/HyperTwistMemoryCoreLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistMemory/HyperTwistMemoryCoreLibrary.cpp @@ -153,6 +153,339 @@ namespace HyperTwistMemoryCoreLibraryInternal break; } } + + void AddUniqueString(TArray& Values, const FString& Value) + { + if (!Value.IsEmpty() && !Values.Contains(Value)) + { + Values.Add(Value); + } + } + + void AddChronicleEvent( + FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState, + const FHyperTwistMemoryChronicleEventEntry& ChronicleEvent + ) + { + if (!ChronicleEvent.IsStructurallyValid() + || ChronicleContinuityState.ChronicleEvents.ContainsByPredicate( + [&ChronicleEvent](const FHyperTwistMemoryChronicleEventEntry& ExistingEvent) + { + return ExistingEvent.EventId == ChronicleEvent.EventId; + })) + { + return; + } + + ChronicleContinuityState.ChronicleEvents.Add(ChronicleEvent); + } + + void AddSessionGroup( + FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState, + const FHyperTwistMemorySessionGroup& SessionGroup + ) + { + if (!SessionGroup.IsStructurallyValid() + || ChronicleContinuityState.SessionGroups.ContainsByPredicate( + [&SessionGroup](const FHyperTwistMemorySessionGroup& ExistingGroup) + { + return ExistingGroup.GroupId == SessionGroup.GroupId; + })) + { + return; + } + + ChronicleContinuityState.SessionGroups.Add(SessionGroup); + } + + void AddResumePack( + FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState, + const FHyperTwistMemoryResumePack& ResumePack + ) + { + if (!ResumePack.IsStructurallyValid() + || ChronicleContinuityState.ResumePacks.ContainsByPredicate( + [&ResumePack](const FHyperTwistMemoryResumePack& ExistingPack) + { + return ExistingPack.PackId == ResumePack.PackId; + })) + { + return; + } + + ChronicleContinuityState.ResumePacks.Add(ResumePack); + } + + void AddContinuityGuard( + FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState, + const FHyperTwistMemoryContinuityGuard& ContinuityGuard + ) + { + if (!ContinuityGuard.IsStructurallyValid() + || ChronicleContinuityState.ContinuityGuards.ContainsByPredicate( + [&ContinuityGuard](const FHyperTwistMemoryContinuityGuard& ExistingGuard) + { + return ExistingGuard.GuardId == ContinuityGuard.GuardId; + })) + { + return; + } + + ChronicleContinuityState.ContinuityGuards.Add(ContinuityGuard); + } + + FString ResolveRunRecordReferenceUtc( + const FHyperTwistTrainingRunRecord& RunRecord, + const FString& ResolvedReferenceUtc + ) + { + if (!RunRecord.LastUpdatedAtUtc.IsEmpty()) + { + return RunRecord.LastUpdatedAtUtc; + } + + if (!RunRecord.Session.EndedAtUtc.IsEmpty()) + { + return RunRecord.Session.EndedAtUtc; + } + + if (!RunRecord.Summary.LastCompletedAtUtc.IsEmpty()) + { + return RunRecord.Summary.LastCompletedAtUtc; + } + + if (!RunRecord.Session.StartedAtUtc.IsEmpty()) + { + return RunRecord.Session.StartedAtUtc; + } + + return ResolvedReferenceUtc; + } + + FString ResolveReviewPlanReferenceUtc( + const FHyperTwistTrainingReviewPlanState& ReviewPlan, + const FString& ResolvedReferenceUtc + ) + { + if (!ReviewPlan.CompletedAtUtc.IsEmpty()) + { + return ReviewPlan.CompletedAtUtc; + } + + if (!ReviewPlan.StartedAtUtc.IsEmpty()) + { + return ReviewPlan.StartedAtUtc; + } + + if (!ReviewPlan.CreatedAtUtc.IsEmpty()) + { + return ReviewPlan.CreatedAtUtc; + } + + if (!ReviewPlan.ReferenceUtc.IsEmpty()) + { + return ReviewPlan.ReferenceUtc; + } + + return ResolvedReferenceUtc; + } + + FString ResolveQueueStateReferenceUtc( + const FHyperTwistTrainingCoachSessionQueueState& QueueState, + const FString& ResolvedReferenceUtc + ) + { + if (!QueueState.LastUpdatedAtUtc.IsEmpty()) + { + return QueueState.LastUpdatedAtUtc; + } + + if (!QueueState.ReferenceUtc.IsEmpty()) + { + return QueueState.ReferenceUtc; + } + + return ResolvedReferenceUtc; + } + + bool HasOpenTrainingSession(const FHyperTwistTrainingRunRecord& RunRecord) + { + return !RunRecord.bCompleted + || RunRecord.Session.SessionState != EHyperTwistTrainingSessionState::Completed + || RunRecord.RemainingCaseIds.Num() > 0; + } + + bool HasOpenReviewPlan(const FHyperTwistTrainingReviewPlanState& ReviewPlan) + { + return !ReviewPlan.bCompleted; + } + + bool IsQueueEntryOpen(const EHyperTwistTrainingCoachSessionQueueEntryState EntryState) + { + return EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Pending + || EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Deferred + || EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::Ready + || EntryState == EHyperTwistTrainingCoachSessionQueueEntryState::InProgress; + } + + bool HasOpenQueue(const FHyperTwistTrainingCoachSessionQueueState& QueueState) + { + return QueueState.PendingEntryCount > 0 + || QueueState.DeferredEntryCount > 0 + || QueueState.ReadyEntryCount > 0 + || QueueState.InProgressEntryCount > 0; + } + + int32 CountOpenReviewEntries(const FHyperTwistTrainingReviewPlanState& ReviewPlan) + { + int32 OpenEntryCount = 0; + for (const FHyperTwistTrainingReviewPlanEntry& Entry : ReviewPlan.Entries) + { + if (Entry.EntryState != EHyperTwistTrainingReviewPlanEntryState::Completed + && Entry.EntryState != EHyperTwistTrainingReviewPlanEntryState::Skipped) + { + ++OpenEntryCount; + } + } + + return OpenEntryCount; + } + + int32 CountOpenQueueEntries(const FHyperTwistTrainingCoachSessionQueueState& QueueState) + { + int32 OpenEntryCount = 0; + for (const FHyperTwistTrainingCoachSessionQueueStateEntry& Entry : QueueState.Entries) + { + if (IsQueueEntryOpen(Entry.EntryState)) + { + ++OpenEntryCount; + } + } + + return OpenEntryCount; + } + + TArray CollectRunFocusCaseIds(const FHyperTwistTrainingRunRecord& RunRecord) + { + TArray FocusCaseIds; + for (const FString& RemainingCaseId : RunRecord.RemainingCaseIds) + { + AddUniqueString(FocusCaseIds, RemainingCaseId); + } + + for (const FHyperTwistTrainingAttempt& Attempt : RunRecord.Attempts) + { + AddUniqueString(FocusCaseIds, Attempt.CaseId); + } + + for (const FHyperTwistTrainingReviewHistoryEntry& ReviewEntry : RunRecord.ReviewHistory) + { + AddUniqueString(FocusCaseIds, ReviewEntry.CaseId); + } + + if (FocusCaseIds.Num() == 0) + { + AddUniqueString(FocusCaseIds, RunRecord.Session.CurrentCaseId); + } + + return FocusCaseIds; + } + + TArray CollectReviewPlanFocusCaseIds( + const FHyperTwistTrainingReviewPlanState& ReviewPlan, + const bool bOpenOnly + ) + { + TArray FocusCaseIds; + for (const FHyperTwistTrainingReviewPlanEntry& Entry : ReviewPlan.Entries) + { + if (bOpenOnly + && Entry.EntryState == EHyperTwistTrainingReviewPlanEntryState::Completed) + { + continue; + } + + AddUniqueString(FocusCaseIds, Entry.CaseId); + } + + return FocusCaseIds; + } + + TArray CollectQueueFocusCaseIds( + const FHyperTwistTrainingCoachSessionQueueState& QueueState, + const bool bOpenOnly + ) + { + TArray FocusCaseIds; + for (const FHyperTwistTrainingCoachSessionQueueStateEntry& Entry : QueueState.Entries) + { + if (bOpenOnly && !IsQueueEntryOpen(Entry.EntryState)) + { + continue; + } + + for (const FString& FocusCaseId : Entry.FocusCaseIds) + { + AddUniqueString(FocusCaseIds, FocusCaseId); + } + } + + return FocusCaseIds; + } + + FString ResolveQueueHistoryEventKindToken( + const EHyperTwistTrainingCoachSessionQueueEventKind EventKind + ) + { + switch (EventKind) + { + case EHyperTwistTrainingCoachSessionQueueEventKind::Queued: + return TEXT("queued"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Ready: + return TEXT("ready"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Started: + return TEXT("started"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Deferred: + return TEXT("deferred"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Promoted: + return TEXT("promoted"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Completed: + return TEXT("completed"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Skipped: + return TEXT("skipped"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Expired: + return TEXT("expired"); + default: + return TEXT("unknown"); + } + } + + FString ResolveQueueHistoryEventLabel( + const EHyperTwistTrainingCoachSessionQueueEventKind EventKind + ) + { + switch (EventKind) + { + case EHyperTwistTrainingCoachSessionQueueEventKind::Queued: + return TEXT("Coach Session Queued"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Ready: + return TEXT("Coach Session Ready"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Started: + return TEXT("Coach Session Started"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Deferred: + return TEXT("Coach Session Deferred"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Promoted: + return TEXT("Coach Session Promoted"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Completed: + return TEXT("Coach Session Completed"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Skipped: + return TEXT("Coach Session Skipped"); + case EHyperTwistTrainingCoachSessionQueueEventKind::Expired: + return TEXT("Coach Session Expired"); + default: + return TEXT("Coach Session Event"); + } + } } FHyperTwistMemoryLedgerState UHyperTwistMemoryCoreLibrary::DeriveMemoryLedgerState( @@ -1316,3 +1649,762 @@ FHyperTwistMemoryLedgerState UHyperTwistMemoryCoreLibrary::DeriveMemoryLedgerSta return LedgerState; } + +FHyperTwistMemoryChronicleContinuityState + UHyperTwistMemoryCoreLibrary::DeriveMemoryChronicleContinuityState( + const FHyperTwistTrainingRepositoryState& RepositoryState, + const FString& UserId, + const FString& ReferenceUtc + ) +{ + FHyperTwistMemoryChronicleContinuityState ChronicleContinuityState; + if (UserId.IsEmpty()) + { + return ChronicleContinuityState; + } + + const FString ResolvedReferenceUtc = + HyperTwistMemoryCoreLibraryInternal::ResolveReferenceUtc(ReferenceUtc); + ChronicleContinuityState.UserId = UserId; + ChronicleContinuityState.ReferenceUtc = ResolvedReferenceUtc; + + const FHyperTwistMemoryLedgerState MemoryLedgerState = DeriveMemoryLedgerState( + RepositoryState, + UserId, + ResolvedReferenceUtc + ); + + TMap LedgerEntitiesById; + for (const FHyperTwistMemoryEntityDescriptor& LedgerEntity : MemoryLedgerState.Entities) + { + LedgerEntitiesById.Add(LedgerEntity.EntityId, LedgerEntity); + } + + TMap> QueueHistoryEventIdsByQueueId; + TMap SessionGroupIdByRunRecordId; + TMap SessionGroupIdByReviewPlanId; + TMap SessionGroupIdByQueueId; + const FString LedgerContractEntityId = FString::Printf( + TEXT("memory-entity/provenance/ledger/current/%s"), + *UserId + ); + + for (const FHyperTwistTrainingRunRecord& RunRecord : RepositoryState.RunRecords) + { + if (!RunRecord.IsStructurallyValid() || RunRecord.Session.UserId != UserId) + { + continue; + } + + const FString RunEntityId = FString::Printf( + TEXT("memory-entity/chronicle/run-record/%s"), + *RunRecord.Session.TrainingSessionId + ); + + FHyperTwistMemoryChronicleEventEntry ChronicleEvent; + ChronicleEvent.EventId = FString::Printf( + TEXT("memory-chronicle-event/run-record/%s"), + *RunRecord.Session.TrainingSessionId + ); + ChronicleEvent.EventKind = TEXT("training-run-record"); + ChronicleEvent.DisplayLabel = + HyperTwistMemoryCoreLibraryInternal::HasOpenTrainingSession(RunRecord) + ? TEXT("Open Training Session") + : TEXT("Completed Training Session"); + ChronicleEvent.UserId = UserId; + ChronicleEvent.DeckId = RunRecord.Session.DeckId; + ChronicleEvent.RootRecordId = RunRecord.Session.TrainingSessionId; + ChronicleEvent.RelatedTrainingSessionId = RunRecord.Session.TrainingSessionId; + ChronicleEvent.RecordedAtUtc = + HyperTwistMemoryCoreLibraryInternal::ResolveRunRecordReferenceUtc( + RunRecord, + ResolvedReferenceUtc + ); + ChronicleEvent.Lane = EHyperTwistMemoryLane::ChronicleCapture; + ChronicleEvent.bOpenContinuity = + HyperTwistMemoryCoreLibraryInternal::HasOpenTrainingSession(RunRecord); + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + RunEntityId, + TEXT("training-run-record"), + ChronicleEvent.ProvenanceLinks + ); + HyperTwistMemoryCoreLibraryInternal::AddChronicleEvent( + ChronicleContinuityState, + ChronicleEvent + ); + + FHyperTwistMemorySessionGroup SessionGroup; + SessionGroup.GroupId = FString::Printf( + TEXT("memory-session-group/training-session/%s"), + *RunRecord.Session.TrainingSessionId + ); + SessionGroup.GroupKind = TEXT("training-session"); + SessionGroup.Headline = ChronicleEvent.DisplayLabel; + SessionGroup.UserId = UserId; + SessionGroup.DeckId = RunRecord.Session.DeckId; + SessionGroup.RootRecordId = RunRecord.Session.TrainingSessionId; + SessionGroup.RootTrainingSessionId = RunRecord.Session.TrainingSessionId; + SessionGroup.ReferenceUtc = ChronicleEvent.RecordedAtUtc; + SessionGroup.ChronicleEventIds = {ChronicleEvent.EventId}; + SessionGroup.FocusCaseIds = + HyperTwistMemoryCoreLibraryInternal::CollectRunFocusCaseIds(RunRecord); + SessionGroup.EventCount = SessionGroup.ChronicleEventIds.Num(); + SessionGroup.bCompleted = !ChronicleEvent.bOpenContinuity; + SessionGroup.bHasResumeCandidate = ChronicleEvent.bOpenContinuity; + SessionGroup.ProvenanceLinks = ChronicleEvent.ProvenanceLinks; + HyperTwistMemoryCoreLibraryInternal::AddSessionGroup( + ChronicleContinuityState, + SessionGroup + ); + SessionGroupIdByRunRecordId.Add( + RunRecord.Session.TrainingSessionId, + SessionGroup.GroupId + ); + + if (ChronicleEvent.bOpenContinuity) + { + FHyperTwistMemoryResumePack ResumePack; + ResumePack.PackId = FString::Printf( + TEXT("memory-resume-pack/training-session/%s"), + *RunRecord.Session.TrainingSessionId + ); + ResumePack.PackKind = TEXT("training-session-resume"); + ResumePack.Headline = TEXT("Resume Training Session"); + ResumePack.UserId = UserId; + ResumePack.DeckId = RunRecord.Session.DeckId; + ResumePack.ResumeTargetId = RunRecord.Session.TrainingSessionId; + ResumePack.ResumeActionId = TEXT("resume-training-session"); + ResumePack.ReferenceUtc = ChronicleEvent.RecordedAtUtc; + ResumePack.ChronicleGroupIds = {SessionGroup.GroupId}; + ResumePack.FocusCaseIds = SessionGroup.FocusCaseIds; + ResumePack.PendingItemCount = FMath::Max(1, RunRecord.RemainingCaseIds.Num()); + ResumePack.bTimeSensitive = + RunRecord.Session.SessionState == EHyperTwistTrainingSessionState::Active; + ResumePack.ProvenanceLinks = ChronicleEvent.ProvenanceLinks; + HyperTwistMemoryCoreLibraryInternal::AddResumePack( + ChronicleContinuityState, + ResumePack + ); + } + } + + for (const FHyperTwistTrainingReviewPlanState& ReviewPlan : RepositoryState.ReviewPlans) + { + if (!ReviewPlan.IsStructurallyValid() || ReviewPlan.UserId != UserId) + { + continue; + } + + const FString ReviewPlanEntityId = FString::Printf( + TEXT("memory-entity/workflow/review-plan/%s"), + *ReviewPlan.PlanId + ); + + FHyperTwistMemoryChronicleEventEntry ChronicleEvent; + ChronicleEvent.EventId = FString::Printf( + TEXT("memory-chronicle-event/review-plan/%s"), + *ReviewPlan.PlanId + ); + ChronicleEvent.EventKind = TEXT("training-review-plan"); + ChronicleEvent.DisplayLabel = + HyperTwistMemoryCoreLibraryInternal::HasOpenReviewPlan(ReviewPlan) + ? TEXT("Open Review Plan") + : TEXT("Completed Review Plan"); + ChronicleEvent.UserId = UserId; + ChronicleEvent.DeckId = ReviewPlan.SourceDeckId; + ChronicleEvent.RootRecordId = ReviewPlan.PlanId; + ChronicleEvent.RelatedReviewPlanId = ReviewPlan.PlanId; + ChronicleEvent.RecordedAtUtc = + HyperTwistMemoryCoreLibraryInternal::ResolveReviewPlanReferenceUtc( + ReviewPlan, + ResolvedReferenceUtc + ); + ChronicleEvent.Lane = EHyperTwistMemoryLane::ContinuityResume; + ChronicleEvent.bOpenContinuity = + HyperTwistMemoryCoreLibraryInternal::HasOpenReviewPlan(ReviewPlan); + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + ReviewPlanEntityId, + TEXT("training-review-plan"), + ChronicleEvent.ProvenanceLinks + ); + HyperTwistMemoryCoreLibraryInternal::AddChronicleEvent( + ChronicleContinuityState, + ChronicleEvent + ); + + FHyperTwistMemorySessionGroup SessionGroup; + SessionGroup.GroupId = FString::Printf( + TEXT("memory-session-group/review-plan/%s"), + *ReviewPlan.PlanId + ); + SessionGroup.GroupKind = TEXT("review-plan"); + SessionGroup.Headline = ChronicleEvent.DisplayLabel; + SessionGroup.UserId = UserId; + SessionGroup.DeckId = ReviewPlan.SourceDeckId; + SessionGroup.RootRecordId = ReviewPlan.PlanId; + SessionGroup.RootReviewPlanId = ReviewPlan.PlanId; + SessionGroup.ReferenceUtc = ChronicleEvent.RecordedAtUtc; + SessionGroup.ChronicleEventIds = {ChronicleEvent.EventId}; + SessionGroup.FocusCaseIds = + HyperTwistMemoryCoreLibraryInternal::CollectReviewPlanFocusCaseIds( + ReviewPlan, + false + ); + SessionGroup.EventCount = SessionGroup.ChronicleEventIds.Num(); + SessionGroup.bCompleted = !ChronicleEvent.bOpenContinuity; + SessionGroup.bHasResumeCandidate = ChronicleEvent.bOpenContinuity; + SessionGroup.ProvenanceLinks = ChronicleEvent.ProvenanceLinks; + HyperTwistMemoryCoreLibraryInternal::AddSessionGroup( + ChronicleContinuityState, + SessionGroup + ); + SessionGroupIdByReviewPlanId.Add(ReviewPlan.PlanId, SessionGroup.GroupId); + + if (ChronicleEvent.bOpenContinuity) + { + FHyperTwistMemoryResumePack ResumePack; + ResumePack.PackId = FString::Printf( + TEXT("memory-resume-pack/review-plan/%s"), + *ReviewPlan.PlanId + ); + ResumePack.PackKind = TEXT("review-plan-resume"); + ResumePack.Headline = TEXT("Resume Review Plan"); + ResumePack.UserId = UserId; + ResumePack.DeckId = ReviewPlan.SourceDeckId; + ResumePack.ResumeTargetId = ReviewPlan.PlanId; + ResumePack.ResumeActionId = TEXT("resume-review-plan"); + ResumePack.ReferenceUtc = ChronicleEvent.RecordedAtUtc; + ResumePack.ChronicleGroupIds = {SessionGroup.GroupId}; + ResumePack.FocusCaseIds = + HyperTwistMemoryCoreLibraryInternal::CollectReviewPlanFocusCaseIds( + ReviewPlan, + true + ); + ResumePack.PendingItemCount = FMath::Max( + 1, + HyperTwistMemoryCoreLibraryInternal::CountOpenReviewEntries(ReviewPlan) + ); + ResumePack.bTimeSensitive = ReviewPlan.StartedAtUtc.IsEmpty(); + ResumePack.ProvenanceLinks = ChronicleEvent.ProvenanceLinks; + HyperTwistMemoryCoreLibraryInternal::AddResumePack( + ChronicleContinuityState, + ResumePack + ); + } + } + + for (const FHyperTwistTrainingCoachSessionQueueHistoryEntry& QueueHistoryEntry : + RepositoryState.StoredCoachSessionQueueHistory) + { + if (!QueueHistoryEntry.IsStructurallyValid() || QueueHistoryEntry.UserId != UserId) + { + continue; + } + + const FString QueueEntityId = FString::Printf( + TEXT("memory-entity/workflow/coach-session-queue/%s"), + *QueueHistoryEntry.QueueId + ); + + FHyperTwistMemoryChronicleEventEntry ChronicleEvent; + ChronicleEvent.EventId = FString::Printf( + TEXT("memory-chronicle-event/coach-session-queue-history/%s"), + *QueueHistoryEntry.HistoryEntryId + ); + ChronicleEvent.EventKind = FString::Printf( + TEXT("coach-session-queue-history/%s"), + *HyperTwistMemoryCoreLibraryInternal::ResolveQueueHistoryEventKindToken( + QueueHistoryEntry.EventKind) + ); + ChronicleEvent.DisplayLabel = + HyperTwistMemoryCoreLibraryInternal::ResolveQueueHistoryEventLabel( + QueueHistoryEntry.EventKind + ); + ChronicleEvent.UserId = UserId; + ChronicleEvent.DeckId = QueueHistoryEntry.FocusDeckId; + ChronicleEvent.RootRecordId = QueueHistoryEntry.HistoryEntryId; + ChronicleEvent.RelatedTrainingSessionId = QueueHistoryEntry.SourceTrainingSessionId; + ChronicleEvent.RelatedQueueId = QueueHistoryEntry.QueueId; + ChronicleEvent.RecordedAtUtc = QueueHistoryEntry.EventAtUtc; + ChronicleEvent.Lane = EHyperTwistMemoryLane::ChronicleCapture; + ChronicleEvent.bOpenContinuity = QueueHistoryEntry.NewEntryState + == EHyperTwistTrainingCoachSessionQueueEntryState::InProgress; + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + QueueEntityId, + TEXT("coach-session-queue"), + ChronicleEvent.ProvenanceLinks + ); + if (!QueueHistoryEntry.SourceTrainingSessionId.IsEmpty()) + { + const FString RunEntityId = FString::Printf( + TEXT("memory-entity/chronicle/run-record/%s"), + *QueueHistoryEntry.SourceTrainingSessionId + ); + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + RunEntityId, + TEXT("training-run-record"), + ChronicleEvent.ProvenanceLinks + ); + } + HyperTwistMemoryCoreLibraryInternal::AddChronicleEvent( + ChronicleContinuityState, + ChronicleEvent + ); + QueueHistoryEventIdsByQueueId.FindOrAdd(QueueHistoryEntry.QueueId).Add( + ChronicleEvent.EventId + ); + } + + for (const FHyperTwistTrainingCoachSessionQueueState& QueueState : + RepositoryState.StoredCoachSessionQueues) + { + if (!QueueState.IsStructurallyValid() || QueueState.UserId != UserId) + { + continue; + } + + const FString QueueEntityId = FString::Printf( + TEXT("memory-entity/workflow/coach-session-queue/%s"), + *QueueState.QueueId + ); + + FHyperTwistMemoryChronicleEventEntry ChronicleEvent; + ChronicleEvent.EventId = FString::Printf( + TEXT("memory-chronicle-event/coach-session-queue/%s"), + *QueueState.QueueId + ); + ChronicleEvent.EventKind = TEXT("coach-session-queue"); + ChronicleEvent.DisplayLabel = + HyperTwistMemoryCoreLibraryInternal::HasOpenQueue(QueueState) + ? TEXT("Open Coach Session Queue") + : TEXT("Completed Coach Session Queue"); + ChronicleEvent.UserId = UserId; + ChronicleEvent.DeckId = QueueState.FocusDeckId; + ChronicleEvent.RootRecordId = QueueState.QueueId; + ChronicleEvent.RelatedTrainingSessionId = QueueState.ActiveTrainingSessionId; + ChronicleEvent.RelatedQueueId = QueueState.QueueId; + ChronicleEvent.RecordedAtUtc = + HyperTwistMemoryCoreLibraryInternal::ResolveQueueStateReferenceUtc( + QueueState, + ResolvedReferenceUtc + ); + ChronicleEvent.Lane = EHyperTwistMemoryLane::ContinuityResume; + ChronicleEvent.bOpenContinuity = + HyperTwistMemoryCoreLibraryInternal::HasOpenQueue(QueueState); + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + QueueEntityId, + TEXT("coach-session-queue"), + ChronicleEvent.ProvenanceLinks + ); + if (!QueueState.ActiveTrainingSessionId.IsEmpty()) + { + const FString RunEntityId = FString::Printf( + TEXT("memory-entity/chronicle/run-record/%s"), + *QueueState.ActiveTrainingSessionId + ); + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + RunEntityId, + TEXT("training-run-record"), + ChronicleEvent.ProvenanceLinks + ); + } + HyperTwistMemoryCoreLibraryInternal::AddChronicleEvent( + ChronicleContinuityState, + ChronicleEvent + ); + + TArray QueueEventIds = {ChronicleEvent.EventId}; + if (const TArray* QueueHistoryEventIds = + QueueHistoryEventIdsByQueueId.Find(QueueState.QueueId)) + { + for (const FString& QueueHistoryEventId : *QueueHistoryEventIds) + { + HyperTwistMemoryCoreLibraryInternal::AddUniqueString( + QueueEventIds, + QueueHistoryEventId + ); + } + } + + FHyperTwistMemorySessionGroup SessionGroup; + SessionGroup.GroupId = FString::Printf( + TEXT("memory-session-group/coach-session-queue/%s"), + *QueueState.QueueId + ); + SessionGroup.GroupKind = TEXT("coach-session-queue"); + SessionGroup.Headline = ChronicleEvent.DisplayLabel; + SessionGroup.UserId = UserId; + SessionGroup.DeckId = QueueState.FocusDeckId; + SessionGroup.RootRecordId = QueueState.QueueId; + SessionGroup.RootQueueId = QueueState.QueueId; + SessionGroup.ReferenceUtc = ChronicleEvent.RecordedAtUtc; + SessionGroup.ChronicleEventIds = QueueEventIds; + SessionGroup.FocusCaseIds = + HyperTwistMemoryCoreLibraryInternal::CollectQueueFocusCaseIds( + QueueState, + false + ); + SessionGroup.EventCount = SessionGroup.ChronicleEventIds.Num(); + SessionGroup.bCompleted = !ChronicleEvent.bOpenContinuity; + SessionGroup.bHasResumeCandidate = ChronicleEvent.bOpenContinuity; + SessionGroup.ProvenanceLinks = ChronicleEvent.ProvenanceLinks; + HyperTwistMemoryCoreLibraryInternal::AddSessionGroup( + ChronicleContinuityState, + SessionGroup + ); + SessionGroupIdByQueueId.Add(QueueState.QueueId, SessionGroup.GroupId); + + if (ChronicleEvent.bOpenContinuity) + { + FHyperTwistMemoryResumePack ResumePack; + ResumePack.PackId = FString::Printf( + TEXT("memory-resume-pack/coach-session-queue/%s"), + *QueueState.QueueId + ); + ResumePack.PackKind = TEXT("coach-session-queue-resume"); + ResumePack.Headline = TEXT("Resume Coach Session Queue"); + ResumePack.UserId = UserId; + ResumePack.DeckId = QueueState.FocusDeckId; + ResumePack.ResumeTargetId = QueueState.QueueId; + ResumePack.ResumeActionId = TEXT("resume-coach-session-queue"); + ResumePack.ReferenceUtc = ChronicleEvent.RecordedAtUtc; + ResumePack.ChronicleGroupIds = {SessionGroup.GroupId}; + ResumePack.FocusCaseIds = + HyperTwistMemoryCoreLibraryInternal::CollectQueueFocusCaseIds( + QueueState, + true + ); + ResumePack.PendingItemCount = FMath::Max( + 1, + HyperTwistMemoryCoreLibraryInternal::CountOpenQueueEntries(QueueState) + ); + ResumePack.bTimeSensitive = !QueueState.ActiveEntryId.IsEmpty(); + ResumePack.ProvenanceLinks = ChronicleEvent.ProvenanceLinks; + HyperTwistMemoryCoreLibraryInternal::AddResumePack( + ChronicleContinuityState, + ResumePack + ); + } + } + + const FHyperTwistTrainingRepositoryIntegrityReport IntegrityReport = + UHyperTwistTrainingRepositoryLibrary::DeriveRepositoryIntegrityReport(RepositoryState); + + const bool bHasGeneralIntegrityIssue = IntegrityReport.InvalidRunRecordCount > 0 + || IntegrityReport.DuplicateRunRecordCount > 0 + || IntegrityReport.InvalidLearnerStateCount > 0 + || IntegrityReport.DuplicateLearnerStateCount > 0 + || IntegrityReport.InvalidReviewPlanCount > 0 + || IntegrityReport.DuplicateReviewPlanCount > 0 + || IntegrityReport.InvalidStoredSessionTemplateCount > 0 + || IntegrityReport.DuplicateStoredSessionTemplateCount > 0 + || IntegrityReport.InvalidMethodDrillFavoriteCount > 0 + || IntegrityReport.DuplicateMethodDrillFavoriteCount > 0 + || IntegrityReport.InvalidMethodDrillBatchCount > 0 + || IntegrityReport.DuplicateMethodDrillBatchCount > 0 + || IntegrityReport.InvalidFriendLinkCount > 0 + || IntegrityReport.DuplicateFriendLinkCount > 0 + || IntegrityReport.InvalidNotificationCount > 0 + || IntegrityReport.DuplicateNotificationCount > 0 + || IntegrityReport.InvalidChallengeCount > 0 + || IntegrityReport.DuplicateChallengeCount > 0; + + const bool bHasReviewContinuityIssue = IntegrityReport.OutOfSyncLearnerStateCount > 0 + || IntegrityReport.OutOfSyncReviewPlanCount > 0; + const bool bHasCoachLineageIssue = IntegrityReport.InvalidCoachActionPlanCount > 0 + || IntegrityReport.DuplicateCoachActionPlanCount > 0 + || IntegrityReport.MissingCoachActionPlanSourceSessionCount > 0 + || IntegrityReport.MissingCoachActionPlanSourceReviewPlanCount > 0 + || IntegrityReport.OrphanedCoachActionPlanParentCount > 0 + || IntegrityReport.OrphanedCoachActionPlanRootCount > 0 + || IntegrityReport.CyclicCoachActionPlanLineageCount > 0 + || IntegrityReport.BrokenCoachActionPlanDepthCount > 0 + || IntegrityReport.OutOfSyncCoachActionPlanCount > 0; + const bool bHasQueueContinuityIssue = IntegrityReport.InvalidCoachSessionQueueCount > 0 + || IntegrityReport.DuplicateCoachSessionQueueCount > 0 + || IntegrityReport.OutOfSyncCoachSessionQueueCount > 0 + || IntegrityReport.MissingCoachSessionQueueSourceSessionCount > 0 + || IntegrityReport.InvalidCoachSessionQueueActiveEntryCount > 0 + || IntegrityReport.MissingCoachSessionQueueActiveSessionCount > 0 + || IntegrityReport.InvalidCoachSessionQueueHistoryCount > 0 + || IntegrityReport.DuplicateCoachSessionQueueHistoryCount > 0 + || IntegrityReport.OutOfSyncCoachSessionQueueHistoryCount > 0 + || IntegrityReport.MissingCoachSessionQueueHistorySourceSessionCount > 0 + || IntegrityReport.MissingCoachSessionQueueHistoryQueueCount > 0 + || IntegrityReport.MissingCoachSessionQueueHistoryEntryCount > 0; + const bool bHasSmartDeviceContinuityIssue = IntegrityReport.InvalidSmartDeviceSessionCount > 0 + || IntegrityReport.DuplicateSmartDeviceSessionCount > 0 + || IntegrityReport.MissingSmartDeviceSessionSourceSessionCount > 0 + || IntegrityReport.InvalidSmartDeviceEventFrameCount > 0 + || IntegrityReport.DuplicateSmartDeviceEventFrameCount > 0 + || IntegrityReport.MissingSmartDeviceEventSourceSessionCount > 0 + || IntegrityReport.MissingSmartDeviceEventSessionRecordCount > 0 + || IntegrityReport.MissingSmartDeviceEventAttemptLinkCount > 0; + + if (bHasGeneralIntegrityIssue) + { + FHyperTwistMemoryContinuityGuard ContinuityGuard; + ContinuityGuard.GuardId = FString::Printf( + TEXT("memory-continuity-guard/repository-integrity/%s"), + *UserId + ); + ContinuityGuard.GuardKind = TEXT("repository-integrity"); + ContinuityGuard.Headline = TEXT("Repository integrity repair required"); + ContinuityGuard.UserId = UserId; + ContinuityGuard.ReferenceUtc = ResolvedReferenceUtc; + ContinuityGuard.Severity = EHyperTwistMemoryContinuityGuardSeverity::Advisory; + ContinuityGuard.bRequiresManualReview = true; + ContinuityGuard.RepairActions = IntegrityReport.RepairActions; + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + LedgerContractEntityId, + TEXT("memory-ledger-contract"), + ContinuityGuard.ProvenanceLinks + ); + HyperTwistMemoryCoreLibraryInternal::AddContinuityGuard( + ChronicleContinuityState, + ContinuityGuard + ); + } + + if (bHasReviewContinuityIssue) + { + FHyperTwistMemoryContinuityGuard ContinuityGuard; + ContinuityGuard.GuardId = FString::Printf( + TEXT("memory-continuity-guard/review-program/%s"), + *UserId + ); + ContinuityGuard.GuardKind = TEXT("review-program-continuity"); + ContinuityGuard.Headline = TEXT("Review continuity requires reconciliation"); + ContinuityGuard.UserId = UserId; + ContinuityGuard.ReferenceUtc = ResolvedReferenceUtc; + ContinuityGuard.Severity = EHyperTwistMemoryContinuityGuardSeverity::Warning; + ContinuityGuard.bRequiresManualReview = true; + ContinuityGuard.RepairActions = IntegrityReport.RepairActions; + for (const FHyperTwistTrainingReviewPlanState& ReviewPlan : RepositoryState.ReviewPlans) + { + HyperTwistMemoryCoreLibraryInternal::AddUniqueString( + ContinuityGuard.RelatedRecordIds, + ReviewPlan.PlanId + ); + if (!ReviewPlan.PlanId.IsEmpty()) + { + const FString ReviewPlanEntityId = FString::Printf( + TEXT("memory-entity/workflow/review-plan/%s"), + *ReviewPlan.PlanId + ); + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + ReviewPlanEntityId, + TEXT("training-review-plan"), + ContinuityGuard.ProvenanceLinks + ); + break; + } + } + HyperTwistMemoryCoreLibraryInternal::AddContinuityGuard( + ChronicleContinuityState, + ContinuityGuard + ); + } + + if (bHasCoachLineageIssue) + { + FHyperTwistMemoryContinuityGuard ContinuityGuard; + ContinuityGuard.GuardId = FString::Printf( + TEXT("memory-continuity-guard/coach-lineage/%s"), + *UserId + ); + ContinuityGuard.GuardKind = TEXT("coach-lineage"); + ContinuityGuard.Headline = TEXT("Coach follow-up lineage is broken"); + ContinuityGuard.UserId = UserId; + ContinuityGuard.ReferenceUtc = ResolvedReferenceUtc; + ContinuityGuard.Severity = EHyperTwistMemoryContinuityGuardSeverity::Blocking; + ContinuityGuard.bBlocksResume = true; + ContinuityGuard.bRequiresManualReview = true; + ContinuityGuard.RepairActions = IntegrityReport.RepairActions; + for (const FHyperTwistTrainingCoachActionPlan& CoachActionPlan : + RepositoryState.StoredCoachActionPlans) + { + HyperTwistMemoryCoreLibraryInternal::AddUniqueString( + ContinuityGuard.RelatedRecordIds, + CoachActionPlan.PlanId + ); + if (!CoachActionPlan.PlanId.IsEmpty()) + { + const FString CoachPlanEntityId = FString::Printf( + TEXT("memory-entity/workflow/coach-action-plan/%s"), + *CoachActionPlan.PlanId + ); + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + CoachPlanEntityId, + TEXT("coach-action-plan"), + ContinuityGuard.ProvenanceLinks + ); + break; + } + } + if (ContinuityGuard.ProvenanceLinks.Num() == 0) + { + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + LedgerContractEntityId, + TEXT("memory-ledger-contract"), + ContinuityGuard.ProvenanceLinks + ); + } + HyperTwistMemoryCoreLibraryInternal::AddContinuityGuard( + ChronicleContinuityState, + ContinuityGuard + ); + } + + if (bHasQueueContinuityIssue) + { + FHyperTwistMemoryContinuityGuard ContinuityGuard; + ContinuityGuard.GuardId = FString::Printf( + TEXT("memory-continuity-guard/coach-session-queue/%s"), + *UserId + ); + ContinuityGuard.GuardKind = TEXT("coach-session-queue"); + ContinuityGuard.Headline = TEXT("Coach session queue continuity is broken"); + ContinuityGuard.UserId = UserId; + ContinuityGuard.ReferenceUtc = ResolvedReferenceUtc; + ContinuityGuard.Severity = EHyperTwistMemoryContinuityGuardSeverity::Blocking; + ContinuityGuard.bBlocksResume = true; + ContinuityGuard.bRequiresManualReview = true; + ContinuityGuard.RepairActions = IntegrityReport.RepairActions; + for (const FHyperTwistTrainingCoachSessionQueueState& QueueState : + RepositoryState.StoredCoachSessionQueues) + { + HyperTwistMemoryCoreLibraryInternal::AddUniqueString( + ContinuityGuard.RelatedRecordIds, + QueueState.QueueId + ); + if (!QueueState.QueueId.IsEmpty()) + { + const FString QueueEntityId = FString::Printf( + TEXT("memory-entity/workflow/coach-session-queue/%s"), + *QueueState.QueueId + ); + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + QueueEntityId, + TEXT("coach-session-queue"), + ContinuityGuard.ProvenanceLinks + ); + break; + } + } + if (ContinuityGuard.ProvenanceLinks.Num() == 0) + { + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + LedgerContractEntityId, + TEXT("memory-ledger-contract"), + ContinuityGuard.ProvenanceLinks + ); + } + HyperTwistMemoryCoreLibraryInternal::AddContinuityGuard( + ChronicleContinuityState, + ContinuityGuard + ); + } + + if (bHasSmartDeviceContinuityIssue) + { + FHyperTwistMemoryContinuityGuard ContinuityGuard; + ContinuityGuard.GuardId = FString::Printf( + TEXT("memory-continuity-guard/smart-device/%s"), + *UserId + ); + ContinuityGuard.GuardKind = TEXT("smart-device-continuity"); + ContinuityGuard.Headline = TEXT("Smart-device continuity requires review"); + ContinuityGuard.UserId = UserId; + ContinuityGuard.ReferenceUtc = ResolvedReferenceUtc; + ContinuityGuard.Severity = EHyperTwistMemoryContinuityGuardSeverity::Warning; + ContinuityGuard.bRequiresManualReview = true; + ContinuityGuard.RepairActions = IntegrityReport.RepairActions; + HyperTwistMemoryCoreLibraryInternal::TryAppendEntityProvenance( + LedgerEntitiesById, + LedgerContractEntityId, + TEXT("memory-ledger-contract"), + ContinuityGuard.ProvenanceLinks + ); + HyperTwistMemoryCoreLibraryInternal::AddContinuityGuard( + ChronicleContinuityState, + ContinuityGuard + ); + } + + for (FHyperTwistMemorySessionGroup& SessionGroup : ChronicleContinuityState.SessionGroups) + { + if ((SessionGroup.GroupKind == TEXT("review-plan") && bHasReviewContinuityIssue) + || (SessionGroup.GroupKind == TEXT("coach-session-queue") + && (bHasCoachLineageIssue || bHasQueueContinuityIssue))) + { + SessionGroup.bRequiresContinuityGuard = true; + } + } + + for (FHyperTwistMemoryResumePack& ResumePack : ChronicleContinuityState.ResumePacks) + { + if ((ResumePack.PackKind == TEXT("review-plan-resume") && bHasReviewContinuityIssue) + || (ResumePack.PackKind == TEXT("coach-session-queue-resume") + && (bHasCoachLineageIssue || bHasQueueContinuityIssue))) + { + ResumePack.bNeedsContinuityGuard = true; + } + } + + for (const FHyperTwistMemoryChronicleEventEntry& ChronicleEvent : + ChronicleContinuityState.ChronicleEvents) + { + if (ChronicleEvent.Lane == EHyperTwistMemoryLane::ChronicleCapture) + { + ++ChronicleContinuityState.ChronicleCaptureEventCount; + } + else if (ChronicleEvent.Lane == EHyperTwistMemoryLane::ContinuityResume) + { + ++ChronicleContinuityState.ContinuityResumeEventCount; + } + } + + for (const FHyperTwistMemorySessionGroup& SessionGroup : + ChronicleContinuityState.SessionGroups) + { + if (!SessionGroup.bCompleted) + { + ++ChronicleContinuityState.OpenSessionGroupCount; + } + } + + ChronicleContinuityState.ResumeRequiredCount = + ChronicleContinuityState.ResumePacks.Num(); + + for (const FHyperTwistMemoryContinuityGuard& ContinuityGuard : + ChronicleContinuityState.ContinuityGuards) + { + if (ContinuityGuard.Severity == EHyperTwistMemoryContinuityGuardSeverity::Blocking) + { + ++ChronicleContinuityState.BlockingGuardCount; + } + } + + ChronicleContinuityState.bHasRepositoryContinuityIssues = + ChronicleContinuityState.ContinuityGuards.Num() > 0; + + if (!ChronicleContinuityState.IsStructurallyValid()) + { + return FHyperTwistMemoryChronicleContinuityState(); + } + + return ChronicleContinuityState; +} diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistBootstrap/HyperTwistContractLibrary.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistBootstrap/HyperTwistContractLibrary.h index db99c4a..d1248e4 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistBootstrap/HyperTwistContractLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistBootstrap/HyperTwistContractLibrary.h @@ -145,6 +145,10 @@ public: UFUNCTION(BlueprintPure, Category = "HyperTwist|Memory") static FHyperTwistMemoryLedgerState MakeSampleTrainingMemoryLedgerState(); + UFUNCTION(BlueprintPure, Category = "HyperTwist|Memory") + static FHyperTwistMemoryChronicleContinuityState + MakeSampleTrainingMemoryChronicleContinuityState(); + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training") static FHyperTwistTrainingRepositoryIntegrityReport MakeSampleTrainingRepositoryIntegrityReport(); @@ -309,6 +313,11 @@ public: UFUNCTION(BlueprintPure, Category = "HyperTwist|Serialization") static FString SerializeMemoryLedgerStateToJson(const FHyperTwistMemoryLedgerState& MemoryLedgerState); + UFUNCTION(BlueprintPure, Category = "HyperTwist|Serialization") + static FString SerializeMemoryChronicleContinuityStateToJson( + const FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState + ); + UFUNCTION(BlueprintPure, Category = "HyperTwist|Serialization") static FString SerializeTrainingTimerExportPacketToJson(const FHyperTwistTrainingTimerExportPacket& TimerExportPacket); @@ -351,6 +360,12 @@ public: UFUNCTION(BlueprintCallable, Category = "HyperTwist|Serialization") static bool DeserializeMemoryLedgerStateFromJson(const FString& Json, FHyperTwistMemoryLedgerState& OutMemoryLedgerState); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Serialization") + static bool DeserializeMemoryChronicleContinuityStateFromJson( + const FString& Json, + FHyperTwistMemoryChronicleContinuityState& OutChronicleContinuityState + ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Serialization") static bool DeserializeTrainingTimerExportPacketFromJson(const FString& Json, FHyperTwistTrainingTimerExportPacket& OutTimerExportPacket); }; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistMemory/HyperTwistMemoryCoreLibrary.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistMemory/HyperTwistMemoryCoreLibrary.h index afadca5..535878e 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistMemory/HyperTwistMemoryCoreLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistMemory/HyperTwistMemoryCoreLibrary.h @@ -18,4 +18,11 @@ public: const FString& UserId, const FString& ReferenceUtc ); + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Memory") + static FHyperTwistMemoryChronicleContinuityState DeriveMemoryChronicleContinuityState( + const FHyperTwistTrainingRepositoryState& RepositoryState, + const FString& UserId, + const FString& ReferenceUtc + ); }; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistMemory/HyperTwistMemoryTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistMemory/HyperTwistMemoryTypes.h index 732834d..1d1fb75 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistMemory/HyperTwistMemoryTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistMemory/HyperTwistMemoryTypes.h @@ -47,6 +47,15 @@ enum class EHyperTwistMemoryContextAssemblyProfile : uint8 MaxRetentionMode UMETA(DisplayName = "Max-Retention Mode") }; +UENUM(BlueprintType) +enum class EHyperTwistMemoryContinuityGuardSeverity : uint8 +{ + None UMETA(DisplayName = "None"), + Advisory UMETA(DisplayName = "Advisory"), + Warning UMETA(DisplayName = "Warning"), + Blocking UMETA(DisplayName = "Blocking") +}; + USTRUCT(BlueprintType) struct FHyperTwistMemoryProvenanceLink { @@ -327,3 +336,423 @@ struct FHyperTwistMemoryLedgerState return TotalEntityCount == Entities.Num(); } }; + +USTRUCT(BlueprintType) +struct FHyperTwistMemoryChronicleEventEntry +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString EventId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString EventKind; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DisplayLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UserId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RootRecordId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RelatedTrainingSessionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RelatedReviewPlanId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RelatedQueueId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecordedAtUtc; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistMemoryLane Lane = EHyperTwistMemoryLane::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bOpenContinuity = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ProvenanceLinks; + + bool IsStructurallyValid() const + { + if (EventId.IsEmpty() + || EventKind.IsEmpty() + || DisplayLabel.IsEmpty() + || UserId.IsEmpty() + || RecordedAtUtc.IsEmpty() + || Lane == EHyperTwistMemoryLane::None) + { + return false; + } + + for (const FHyperTwistMemoryProvenanceLink& ProvenanceLink : ProvenanceLinks) + { + if (!ProvenanceLink.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistMemorySessionGroup +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GroupId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GroupKind; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UserId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RootRecordId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RootTrainingSessionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RootReviewPlanId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RootQueueId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReferenceUtc; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ChronicleEventIds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray FocusCaseIds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 EventCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCompleted = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasResumeCandidate = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRequiresContinuityGuard = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ProvenanceLinks; + + bool IsStructurallyValid() const + { + if (GroupId.IsEmpty() + || GroupKind.IsEmpty() + || Headline.IsEmpty() + || UserId.IsEmpty() + || RootRecordId.IsEmpty() + || ReferenceUtc.IsEmpty() + || ChronicleEventIds.Num() == 0 + || EventCount != ChronicleEventIds.Num()) + { + return false; + } + + for (const FHyperTwistMemoryProvenanceLink& ProvenanceLink : ProvenanceLinks) + { + if (!ProvenanceLink.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistMemoryResumePack +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PackId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PackKind; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UserId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ResumeTargetId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ResumeActionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReferenceUtc; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistMemoryLane Lane = EHyperTwistMemoryLane::ContinuityResume; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ChronicleGroupIds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray FocusCaseIds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 PendingItemCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bNeedsContinuityGuard = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bTimeSensitive = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ProvenanceLinks; + + bool IsStructurallyValid() const + { + if (PackId.IsEmpty() + || PackKind.IsEmpty() + || Headline.IsEmpty() + || UserId.IsEmpty() + || ResumeTargetId.IsEmpty() + || ResumeActionId.IsEmpty() + || ReferenceUtc.IsEmpty() + || Lane == EHyperTwistMemoryLane::None + || ChronicleGroupIds.Num() == 0 + || PendingItemCount < 0) + { + return false; + } + + for (const FHyperTwistMemoryProvenanceLink& ProvenanceLink : ProvenanceLinks) + { + if (!ProvenanceLink.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistMemoryContinuityGuard +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GuardId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GuardKind; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UserId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReferenceUtc; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistMemoryLane Lane = EHyperTwistMemoryLane::ContinuityResume; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistMemoryContinuityGuardSeverity Severity = + EHyperTwistMemoryContinuityGuardSeverity::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray RelatedRecordIds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray RepairActions; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bBlocksResume = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRequiresManualReview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ProvenanceLinks; + + bool IsStructurallyValid() const + { + if (GuardId.IsEmpty() + || GuardKind.IsEmpty() + || Headline.IsEmpty() + || UserId.IsEmpty() + || ReferenceUtc.IsEmpty() + || Lane == EHyperTwistMemoryLane::None + || Severity == EHyperTwistMemoryContinuityGuardSeverity::None) + { + return false; + } + + for (const FHyperTwistMemoryProvenanceLink& ProvenanceLink : ProvenanceLinks) + { + if (!ProvenanceLink.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistMemoryChronicleContinuityState +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UserId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ReferenceUtc; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ChronicleEvents; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray SessionGroups; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ResumePacks; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ContinuityGuards; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ChronicleCaptureEventCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ContinuityResumeEventCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 OpenSessionGroupCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ResumeRequiredCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BlockingGuardCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasRepositoryContinuityIssues = false; + + bool IsStructurallyValid() const + { + if (UserId.IsEmpty() + || ReferenceUtc.IsEmpty() + || (ChronicleEvents.Num() == 0 + && SessionGroups.Num() == 0 + && ResumePacks.Num() == 0 + && ContinuityGuards.Num() == 0)) + { + return false; + } + + for (const FHyperTwistMemoryChronicleEventEntry& ChronicleEvent : ChronicleEvents) + { + if (!ChronicleEvent.IsStructurallyValid()) + { + return false; + } + } + + for (const FHyperTwistMemorySessionGroup& SessionGroup : SessionGroups) + { + if (!SessionGroup.IsStructurallyValid()) + { + return false; + } + } + + for (const FHyperTwistMemoryResumePack& ResumePack : ResumePacks) + { + if (!ResumePack.IsStructurallyValid()) + { + return false; + } + } + + for (const FHyperTwistMemoryContinuityGuard& ContinuityGuard : ContinuityGuards) + { + if (!ContinuityGuard.IsStructurallyValid()) + { + return false; + } + } + + int32 ComputedChronicleCaptureEventCount = 0; + int32 ComputedContinuityResumeEventCount = 0; + for (const FHyperTwistMemoryChronicleEventEntry& ChronicleEvent : ChronicleEvents) + { + if (ChronicleEvent.Lane == EHyperTwistMemoryLane::ChronicleCapture) + { + ++ComputedChronicleCaptureEventCount; + } + else if (ChronicleEvent.Lane == EHyperTwistMemoryLane::ContinuityResume) + { + ++ComputedContinuityResumeEventCount; + } + } + + int32 ComputedOpenSessionGroupCount = 0; + for (const FHyperTwistMemorySessionGroup& SessionGroup : SessionGroups) + { + if (!SessionGroup.bCompleted) + { + ++ComputedOpenSessionGroupCount; + } + } + + int32 ComputedBlockingGuardCount = 0; + for (const FHyperTwistMemoryContinuityGuard& ContinuityGuard : ContinuityGuards) + { + if (ContinuityGuard.Severity == EHyperTwistMemoryContinuityGuardSeverity::Blocking) + { + ++ComputedBlockingGuardCount; + } + } + + return ChronicleCaptureEventCount == ComputedChronicleCaptureEventCount + && ContinuityResumeEventCount == ComputedContinuityResumeEventCount + && OpenSessionGroupCount == ComputedOpenSessionGroupCount + && ResumeRequiredCount == ResumePacks.Num() + && BlockingGuardCount == ComputedBlockingGuardCount; + } +}; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistMemoryPhase6RM2ChronicleContinuityContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistMemoryPhase6RM2ChronicleContinuityContractTest.cpp new file mode 100644 index 0000000..0d8a927 --- /dev/null +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistMemoryPhase6RM2ChronicleContinuityContractTest.cpp @@ -0,0 +1,363 @@ +// Copyright HyperTwist, Inc. All Rights Reserved. + +#include "Misc/AutomationTest.h" + +#include "HyperTwistBootstrap/HyperTwistContractLibrary.h" +#include "HyperTwistMemory/HyperTwistMemoryCoreLibrary.h" + +#if WITH_AUTOMATION_TESTS + +namespace HyperTwistMemoryPhase6RM2TestInternal +{ + const FHyperTwistMemoryChronicleEventEntry* FindChronicleEventByKind( + const FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState, + const FString& EventKind + ) + { + for (const FHyperTwistMemoryChronicleEventEntry& ChronicleEvent : + ChronicleContinuityState.ChronicleEvents) + { + if (ChronicleEvent.EventKind == EventKind) + { + return &ChronicleEvent; + } + } + + return nullptr; + } + + const FHyperTwistMemoryResumePack* FindResumePackByKind( + const FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState, + const FString& PackKind + ) + { + for (const FHyperTwistMemoryResumePack& ResumePack : + ChronicleContinuityState.ResumePacks) + { + if (ResumePack.PackKind == PackKind) + { + return &ResumePack; + } + } + + return nullptr; + } + + const FHyperTwistMemoryContinuityGuard* FindGuardByKind( + const FHyperTwistMemoryChronicleContinuityState& ChronicleContinuityState, + const FString& GuardKind + ) + { + for (const FHyperTwistMemoryContinuityGuard& ContinuityGuard : + ChronicleContinuityState.ContinuityGuards) + { + if (ContinuityGuard.GuardKind == GuardKind) + { + return &ContinuityGuard; + } + } + + return nullptr; + } +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistMemoryPhase6RM2ChronicleGroupingTest, + "HyperTwist.FirstParty.Memory.Phase6R.M2.ChronicleGrouping", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistMemoryPhase6RM2ChronicleGroupingTest::RunTest(const FString& Parameters) +{ + const FHyperTwistMemoryChronicleContinuityState ChronicleContinuityState = + UHyperTwistContractLibrary::MakeSampleTrainingMemoryChronicleContinuityState(); + + TestTrue( + TEXT("The Phase 6R-M2 chronicle/continuity state must be structurally valid."), + ChronicleContinuityState.IsStructurallyValid() + ); + TestTrue( + TEXT("The Phase 6R-M2 state must expose chronicle capture events."), + ChronicleContinuityState.ChronicleCaptureEventCount > 0 + ); + TestTrue( + TEXT("The Phase 6R-M2 state must expose continuity resume events."), + ChronicleContinuityState.ContinuityResumeEventCount > 0 + ); + TestTrue( + TEXT("The Phase 6R-M2 state must expose grouped continuity sessions."), + ChronicleContinuityState.SessionGroups.Num() >= 3 + ); + TestTrue( + TEXT("The Phase 6R-M2 state must expose bounded resume packs."), + ChronicleContinuityState.ResumePacks.Num() >= 2 + ); + TestEqual( + TEXT("The canonical sample should not emit continuity guards."), + ChronicleContinuityState.ContinuityGuards.Num(), + 0 + ); + + const FHyperTwistMemoryChronicleEventEntry* RunEvent = + HyperTwistMemoryPhase6RM2TestInternal::FindChronicleEventByKind( + ChronicleContinuityState, + TEXT("training-run-record") + ); + TestNotNull(TEXT("A training run-record chronicle event must exist."), RunEvent); + if (RunEvent != nullptr) + { + TestEqual( + TEXT("The run-record chronicle event must stay in the chronicle lane."), + RunEvent->Lane, + EHyperTwistMemoryLane::ChronicleCapture + ); + } + + const FHyperTwistMemoryResumePack* ReviewResumePack = + HyperTwistMemoryPhase6RM2TestInternal::FindResumePackByKind( + ChronicleContinuityState, + TEXT("review-plan-resume") + ); + TestNotNull(TEXT("A review-plan resume pack must exist."), ReviewResumePack); + if (ReviewResumePack != nullptr) + { + TestEqual( + TEXT("The review resume pack must stay in the continuity lane."), + ReviewResumePack->Lane, + EHyperTwistMemoryLane::ContinuityResume + ); + TestTrue( + TEXT("The review resume pack must preserve a grouped chronicle link."), + ReviewResumePack->ChronicleGroupIds.Num() > 0 + ); + } + + const FHyperTwistMemoryResumePack* QueueResumePack = + HyperTwistMemoryPhase6RM2TestInternal::FindResumePackByKind( + ChronicleContinuityState, + TEXT("coach-session-queue-resume") + ); + TestNotNull(TEXT("A coach-session-queue resume pack must exist."), QueueResumePack); + if (QueueResumePack != nullptr) + { + TestTrue( + TEXT("The queue resume pack must keep pending continuity work."), + QueueResumePack->PendingItemCount > 0 + ); + } + + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistMemoryPhase6RM2ContinuityGuardsTest, + "HyperTwist.FirstParty.Memory.Phase6R.M2.ContinuityGuards", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistMemoryPhase6RM2ContinuityGuardsTest::RunTest(const FString& Parameters) +{ + FHyperTwistTrainingRepositoryState RepositoryState = + UHyperTwistContractLibrary::MakeSampleTrainingRepositoryState(); + const FString UserId = RepositoryState.RunRecords.Num() > 0 + ? RepositoryState.RunRecords[0].Session.UserId + : FString(); + TestFalse(TEXT("The continuity-guard sample must resolve a user id."), UserId.IsEmpty()); + if (UserId.IsEmpty()) + { + return false; + } + + const FHyperTwistTrainingCoachActionPlan CoachActionPlan = + UHyperTwistContractLibrary::MakeSampleTrainingCoachActionPlan(); + if (CoachActionPlan.IsStructurallyValid()) + { + RepositoryState.StoredCoachActionPlans.Add(CoachActionPlan); + + FHyperTwistTrainingCoachActionPlan BrokenLineagePlan = CoachActionPlan; + BrokenLineagePlan.PlanId = TEXT("coach_plan_broken_lineage"); + BrokenLineagePlan.ParentPlanId = TEXT("missing_parent_plan"); + BrokenLineagePlan.RootPlanId = TEXT("missing_root_plan"); + BrokenLineagePlan.SourceSessionId = TEXT("missing_training_session"); + BrokenLineagePlan.SourceReviewPlanId = TEXT("missing_review_plan"); + BrokenLineagePlan.FollowUpDepth = 3; + RepositoryState.StoredCoachActionPlans.Add(BrokenLineagePlan); + } + + const FHyperTwistTrainingCoachSessionQueueState QueueState = + UHyperTwistContractLibrary::MakeSampleTrainingCoachSessionQueueState(); + TestTrue(TEXT("The queue sample must be structurally valid."), QueueState.IsStructurallyValid()); + if (!QueueState.IsStructurallyValid()) + { + return false; + } + + RepositoryState.StoredCoachSessionQueues.Add(QueueState); + + FHyperTwistTrainingCoachSessionQueueState BrokenQueueState = QueueState; + BrokenQueueState.QueueId = TEXT("coach_queue_broken"); + BrokenQueueState.ActiveEntryId = TEXT("missing_active_entry"); + BrokenQueueState.ActiveTrainingSessionId = TEXT("missing_training_session"); + if (BrokenQueueState.Entries.Num() > 0) + { + BrokenQueueState.Entries[0].EntryState = + EHyperTwistTrainingCoachSessionQueueEntryState::InProgress; + BrokenQueueState.Entries[0].SourceTrainingSessionId = TEXT("missing_training_session"); + BrokenQueueState.Entries[0].StartedAtUtc = TEXT("2026-04-28T12:15:00Z"); + } + RepositoryState.StoredCoachSessionQueues.Add(BrokenQueueState); + + FHyperTwistTrainingCoachSessionQueueHistoryEntry BrokenHistoryEntry; + BrokenHistoryEntry.HistoryEntryId = TEXT("history_missing_training_session"); + BrokenHistoryEntry.QueueId = BrokenQueueState.QueueId; + BrokenHistoryEntry.UserId = BrokenQueueState.UserId; + BrokenHistoryEntry.EntryId = BrokenQueueState.Entries.Num() > 0 + ? BrokenQueueState.Entries[0].EntryId + : TEXT("missing_entry"); + BrokenHistoryEntry.SourceLabel = BrokenQueueState.Entries.Num() > 0 + ? BrokenQueueState.Entries[0].SourceLabel + : TEXT("coach_queue_primary"); + BrokenHistoryEntry.WorkFingerprint = BrokenQueueState.Entries.Num() > 0 + ? BrokenQueueState.Entries[0].WorkFingerprint + : TEXT("broken_history_work"); + BrokenHistoryEntry.EventKind = EHyperTwistTrainingCoachSessionQueueEventKind::Started; + BrokenHistoryEntry.EventAtUtc = TEXT("2026-04-28T12:15:00Z"); + BrokenHistoryEntry.PreviousEntryState = + EHyperTwistTrainingCoachSessionQueueEntryState::Ready; + BrokenHistoryEntry.NewEntryState = + EHyperTwistTrainingCoachSessionQueueEntryState::InProgress; + BrokenHistoryEntry.SourceTrainingSessionId = TEXT("missing_training_session"); + BrokenHistoryEntry.FocusDeckId = BrokenQueueState.FocusDeckId; + BrokenHistoryEntry.FocusMethodSegmentId = BrokenQueueState.FocusMethodSegmentId; + if (BrokenQueueState.Entries.Num() > 0) + { + BrokenHistoryEntry.FocusCaseIds = BrokenQueueState.Entries[0].FocusCaseIds; + } + RepositoryState.StoredCoachSessionQueueHistory.Add(BrokenHistoryEntry); + + FHyperTwistTrainingCoachSessionQueueHistoryEntry OrphanedEntryHistoryEntry = + BrokenHistoryEntry; + OrphanedEntryHistoryEntry.HistoryEntryId = TEXT("history_missing_entry_link"); + OrphanedEntryHistoryEntry.QueueId = QueueState.QueueId; + OrphanedEntryHistoryEntry.EntryId = TEXT("missing_entry_link"); + RepositoryState.StoredCoachSessionQueueHistory.Add(OrphanedEntryHistoryEntry); + + const FHyperTwistMemoryChronicleContinuityState ChronicleContinuityState = + UHyperTwistMemoryCoreLibrary::DeriveMemoryChronicleContinuityState( + RepositoryState, + UserId, + TEXT("2026-04-28T12:40:00Z") + ); + + TestTrue( + TEXT("The continuity-guard derivation must remain structurally valid."), + ChronicleContinuityState.IsStructurallyValid() + ); + TestTrue( + TEXT("Broken continuity inputs must emit repository continuity guards."), + ChronicleContinuityState.bHasRepositoryContinuityIssues + ); + TestTrue( + TEXT("Broken continuity inputs must emit at least one blocking guard."), + ChronicleContinuityState.BlockingGuardCount > 0 + ); + + const FHyperTwistMemoryContinuityGuard* QueueGuard = + HyperTwistMemoryPhase6RM2TestInternal::FindGuardByKind( + ChronicleContinuityState, + TEXT("coach-session-queue") + ); + TestNotNull(TEXT("A coach-session-queue continuity guard must exist."), QueueGuard); + if (QueueGuard != nullptr) + { + TestEqual( + TEXT("The queue continuity guard must remain blocking."), + QueueGuard->Severity, + EHyperTwistMemoryContinuityGuardSeverity::Blocking + ); + } + + const FHyperTwistMemoryResumePack* QueueResumePack = + HyperTwistMemoryPhase6RM2TestInternal::FindResumePackByKind( + ChronicleContinuityState, + TEXT("coach-session-queue-resume") + ); + TestNotNull(TEXT("A queue resume pack must still be produced."), QueueResumePack); + if (QueueResumePack != nullptr) + { + TestTrue( + TEXT("Queue continuity issues must mark the queue resume pack for guard review."), + QueueResumePack->bNeedsContinuityGuard + ); + } + + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistMemoryPhase6RM2SerializationRoundTripTest, + "HyperTwist.FirstParty.Memory.Phase6R.M2.SerializationRoundTrip", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistMemoryPhase6RM2SerializationRoundTripTest::RunTest(const FString& Parameters) +{ + const FHyperTwistMemoryChronicleContinuityState ChronicleContinuityState = + UHyperTwistContractLibrary::MakeSampleTrainingMemoryChronicleContinuityState(); + TestTrue( + TEXT("The Phase 6R-M2 chronicle/continuity state must be structurally valid."), + ChronicleContinuityState.IsStructurallyValid() + ); + + const FString Json = + UHyperTwistContractLibrary::SerializeMemoryChronicleContinuityStateToJson( + ChronicleContinuityState + ); + TestFalse(TEXT("The serialized M2 JSON must not be empty."), Json.IsEmpty()); + TestTrue( + TEXT("The serialized M2 JSON must expose the review-plan resume pack kind."), + Json.Contains(TEXT("review-plan-resume")) + ); + + FHyperTwistMemoryChronicleContinuityState RoundTrippedState; + TestTrue( + TEXT("The M2 JSON must deserialize cleanly."), + UHyperTwistContractLibrary::DeserializeMemoryChronicleContinuityStateFromJson( + Json, + RoundTrippedState + ) + ); + TestTrue( + TEXT("The round-tripped M2 state must remain structurally valid."), + RoundTrippedState.IsStructurallyValid() + ); + TestEqual( + TEXT("The round-tripped M2 state must preserve chronicle event counts."), + RoundTrippedState.ChronicleEvents.Num(), + ChronicleContinuityState.ChronicleEvents.Num() + ); + TestEqual( + TEXT("The round-tripped M2 state must preserve session group counts."), + RoundTrippedState.SessionGroups.Num(), + ChronicleContinuityState.SessionGroups.Num() + ); + TestEqual( + TEXT("The round-tripped M2 state must preserve resume pack counts."), + RoundTrippedState.ResumePacks.Num(), + ChronicleContinuityState.ResumePacks.Num() + ); + TestEqual( + TEXT("The round-tripped M2 state must preserve continuity guard counts."), + RoundTrippedState.ContinuityGuards.Num(), + ChronicleContinuityState.ContinuityGuards.Num() + ); + TestEqual( + TEXT("The round-tripped M2 state must preserve blocking-guard counts."), + RoundTrippedState.BlockingGuardCount, + ChronicleContinuityState.BlockingGuardCount + ); + + return true; +} + +#endif diff --git a/docs/HYPERTWIST_IMPLEMENTED_RETAINED_AND_NON_IMPLEMENTABLE_REPO_ROUTE_BOARD_2026-05-28.md b/docs/HYPERTWIST_IMPLEMENTED_RETAINED_AND_NON_IMPLEMENTABLE_REPO_ROUTE_BOARD_2026-05-28.md new file mode 100644 index 0000000..5b19d85 --- /dev/null +++ b/docs/HYPERTWIST_IMPLEMENTED_RETAINED_AND_NON_IMPLEMENTABLE_REPO_ROUTE_BOARD_2026-05-28.md @@ -0,0 +1,136 @@ +# HyperTwist implemented, retained, and non-implementable repo route board + +Created on `2026-05-28` + +## Purpose + +This board records the exact current HyperTwist repo-route split so the legal +audit `MIT` count is not confused with the live implementation count. + +Current scope split: + +- canonical closed board: `75` rows +- later intake rows outside that closed board: `1` current row +- live implemented rows: `33` +- selected-not-live permissive retained rows: `29` +- benchmark / oracle / comparison / discard rows: `13` + +The legal-evidence `MIT` count is a root-license signal count only. It is not +the live implementation count and it is not the clean-room backlog count. + +## Live implemented now + +### Permissive live lanes (`21`) + +- `HactarCE/Hyperspeedcube` — `MIT` +- `kkoomen/qbr` — `MIT` +- `vivaansinghvi07/rubix-cube-solver` — `MIT` +- `tao-yu/Alg-Trainer` — `MIT` +- `Lykos/cube_trainer` — `MIT` +- `roice3/Magic120Cell` — `MIT` +- `roice3/MagicCube5D` — `MIT` +- `abunickabhi/5style-Trainer` — `MIT` +- `newyork-anthonyng/rubiks-cross-trainer` — `MIT` +- `Hypercubers/hypercubing.xyz` — `MIT` +- `Aarav2709/KubeTimr` — `MIT` +- `roice3/MagicTile` — `MIT` +- `SYSTRAN/faster-whisper` — `MIT` +- `ggml-org/whisper.cpp` — `MIT` +- `rhasspy/piper` — `MIT` code; voice artifacts reviewed separately +- `met4citizen/TalkingHead` — `MIT` +- `apache/echarts` — `Apache-2.0` +- `google/model-viewer` — `Apache-2.0` +- `mrdoob/three.js` — `MIT` +- `pmndrs/react-three-fiber` — `MIT` +- `pmndrs/xr` — `MIT` + +### Boundary-sensitive live lanes (`7`) + +- `cubing/cubing.js` — `MPL-2.0 OR GPL-3.0-or-later` +- `cutelyaware/magiccube4d` — custom broad-use license with attribution requested +- `PostHog/posthog` — `MIT` outside `ee/`; enterprise-restricted in `ee/` +- `coqui-ai/TTS` — `MPL-2.0` code; mixed model payload licenses +- `screenpipe/screenpipe` — `MIT OR Apache-2.0` core; enterprise-restricted `ee/` +- `remotion-dev/remotion` — custom two-tier commercial license +- `google/model-viewer/packages/shared-assets` — `Apache-2.0` container; mixed per-asset terms + +### Restrictive clean-room live lanes (`5`) + +- `cubing/alg.js` — `GPL-3.0-or-later` +- `cubing/twisty.js` — `GPL-3.0-or-later` +- `kash/cubedesk` — `GPLv3-or-later` in `README` / `LICENSE`; `package.json` says `All Rights Reserved` +- `onionhoney/roux-trainers` — `GPL-3.0` +- `HactarCE/2x2x2x2-Scrambler` — `GPL-3.0` + +## Selected not live, but still retained + +### Closed-board permissive retained rows (`29`) + +- `cahidenes/rubiks-cube-solver` — `MIT` +- `tentone/rubix-solver` — `MIT` +- `NuiLab/code-vr` — `MIT` +- `ecomfe/echarts-gl` — `BSD-3-Clause` +- `KhronosGroup/glTF-Sample-Viewer` — `Apache-2.0` +- `pmndrs/postprocessing` — `Zlib` +- `pmndrs/drei` — `MIT` +- `pmndrs/uikit` — `MIT` +- `pmndrs/react-spring` — `MIT` +- `ecomfe/zrender` — `BSD-3-Clause` +- `pissang/claygl` — `BSD-style permissive` +- `pissang/clay-viewer` — `BSD-3-Clause` +- `KhronosGroup/glTF-Sample-Renderer` — `Apache-2.0` +- `google/model-viewer/packages/space-opera` — `Apache-2.0` +- `google/model-viewer/packages/render-fidelity-tools` — `Apache-2.0` +- `google/model-viewer/packages/model-viewer-effects` — `Apache-2.0` +- `google/model-viewer/packages/modelviewer.dev` — `Apache-2.0` +- `pmndrs/react-postprocessing` — `MIT` +- `pmndrs/three-stdlib` — `MIT` +- `pmndrs/maath` — `MIT` +- `pmndrs/zustand` — `MIT` +- `pmndrs/leva` — `MIT` +- `pmndrs/use-gesture` — `MIT` +- `@react-spring/parallax` — `MIT` +- `@react-spring/rafz` — `MIT` +- `@react-spring/animated` — `MIT` +- `@react-spring/core` — `MIT` +- `@react-spring/shared` — `MIT` +- `@react-spring/types` — `MIT` + +### Later intake retained outside the closed board (`1`) + +- `cjpais/Handy` — `MIT` + - current role: later permissive bounded sidecar only + - retained exact slice: external offline dictation shell, transcript history, + output routing, local model catalog/download/checksum/unload policy + patterns, and optional transcript post-process overlay + +## Not supposed to be directly implemented from source + +### Benchmark / oracle / comparison / clean-room input only (`10`) + +- `poliva/cubedex` — no clear root license signal; restrictive comparison row only +- `cs0x7f/cstimer` — `GPL-3.0`; benchmark / oracle only +- `ShellPuppy/RCube` — `GPL-3.0`; retained large-`N` strategy benchmark only +- `brownan/Rubiks-Cube-Solver` — `GPL-3.0`; solver oracle only +- `vwcwong/CubeSim` — `GPL-3.0`; readable state/history benchmark only +- `AviKaufman/Rubix-cube-trainer` — `All Rights Reserved`; clean-room benchmark only +- `alinen/cube` — no clear root license signal; clean-room benchmark only +- `ambisinister/blindsolve` — no clear root license signal; clean-room benchmark only +- `efrantar/rob-twophase` — `GPL-3.0`; solver oracle only +- `yakupbilen/drl-rubiks-cube` — `MIT`; research benchmark only + +### Discarded / off-domain / superseded (`3`) + +- `aMonteSl/CodeXR` — `GPL-3.0-only`; discard-confirmed +- `brianpeiris/RiftSketch` — `MIT`; discard-confirmed +- `MathewKJ2048/Rubiks-cube-simulator` — `GPL-3.0`; duplicate-retired historical row + +## Practical interpretation + +- HyperTwist does not have an open restrictive implementation backlog hidden + behind the legal-audit `MIT` count. +- The active clean-roomed and live restrictive set is exactly `5` rows at the + currently justified bounds. +- Additional restrictive or no-clear-license rows remain only as + benchmark / oracle / comparison / discard surfaces unless a later bounded + clean-room route is explicitly reopened. diff --git a/docs/arch/HYPERTWIST_PHASE6R_M2_FIRST_PARTY_CHRONICLE_AND_CONTINUITY_IMPLEMENTATION_PACKET_2026-05-28.md b/docs/arch/HYPERTWIST_PHASE6R_M2_FIRST_PARTY_CHRONICLE_AND_CONTINUITY_IMPLEMENTATION_PACKET_2026-05-28.md new file mode 100644 index 0000000..758fa5f --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_M2_FIRST_PARTY_CHRONICLE_AND_CONTINUITY_IMPLEMENTATION_PACKET_2026-05-28.md @@ -0,0 +1,137 @@ +# HyperTwist Phase 6R-M2 first-party chronicle and continuity implementation packet + +Created on `2026-05-28` + +## Status + +- first-party HyperTwist packet +- bounded `Phase 6R-M2` implementation slice + +## Purpose + +This packet lands the bounded first-party chronicle and continuity seam under +the canonical `Memory Lanes` doctrine. + +The landed slice is: + +- chronicle ledger +- session grouping +- resume packs +- repo-aware continuity guards + +It is not: + +- a broad memory federation packet +- a recall/history-search packet +- a shared-context widening packet +- a cognitive extraction packet +- a knowledge-promotion or user-notes packet +- a compact reducer/digest packet + +## Current authority basis + +This implementation packet stands on: + +- `docs/ops/HYPERTWIST_MEMORY_LANE_AUTHORITY_AND_PHASE_IMPLEMENTATION_DOCTRINE_2026-05-21.md` +- `docs/ops/HYPERTWIST_CONTINUITY_LATTICE_AND_CONTEXT_ASSEMBLY_PROFILE_DOCTRINE_2026-05-23.md` +- `docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md` +- `docs/v6_5_deep_manual_pack/HyperTwist/ARCHITECTURE.md` +- `docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md` +- `docs/v6_5_deep_manual_pack/HyperTwist/PROVENANCE_AND_TRUST_MODEL.md` +- `docs/arch/HYPERTWIST_PHASE6R_M2_FIRST_PARTY_CHRONICLE_AND_CONTINUITY_PREPARATION_PACKET_2026-05-28.md` + +The preserved owners do not change: + +- first-party HyperTwist remains the top-level owner for live chronicle, + continuity, resume-pack, and guard product surfaces +- the landed `Phase 6R-M1` ledger remains the provenance substrate beneath this + packet +- current training, replay, coaching, and repository code remains the raw + evidence substrate beneath this packet +- the VectorShell memory doctrine remains the external synchronization + entrypoint and authority map, not a frozen imported implementation + +## Landed scope + +The current code now owns a bounded first-party chronicle and continuity seam +through: + +- canonical chronicle event, session-group, resume-pack, continuity-guard, and + chronicle/continuity state types in: + - `HyperTwistMemoryTypes.h` +- first-party chronicle/continuity derivation from current training repository + state in: + - `UHyperTwistMemoryCoreLibrary` +- current bounded chronicle coverage for: + - run records + - review plans + - coach session queues + - coach session queue history entries +- current bounded session-group coverage for: + - training sessions + - review plans + - coach session queues +- current bounded resume-pack coverage for: + - open training sessions + - open review plans + - open coach session queues +- current bounded continuity-guard coverage for: + - repository integrity repair + - review-program continuity reconciliation + - coach follow-up lineage repair + - coach session queue continuity repair + - smart-device continuity review +- sample contract helpers in: + - `UHyperTwistContractLibrary::MakeSampleTrainingMemoryChronicleContinuityState()` + - `UHyperTwistContractLibrary::SerializeMemoryChronicleContinuityStateToJson(...)` + - `UHyperTwistContractLibrary::DeserializeMemoryChronicleContinuityStateFromJson(...)` +- focused automation coverage in: + - `HyperTwistMemoryPhase6RM2ChronicleContinuityContractTest.cpp` + +## Why this is still intentionally bounded + +This packet lands chronicle and continuity only. + +Still deferred: + +- explicit recall/history search and provenance drill-down +- shared-context widening +- cognitive extraction, contradiction handling, supersession, or confidence + decay +- curated knowledge-promotion flows +- user-authored notes +- derived compact reducers, digests, and reduced-context packets + +## Validation + +Build validation: + +- `UnrealHyperTwistEditor Win64 Development` + +Focused automation validation: + +- `HyperTwist.FirstParty.Memory.Phase6R.M2.ChronicleGrouping` +- `HyperTwist.FirstParty.Memory.Phase6R.M2.ContinuityGuards` +- `HyperTwist.FirstParty.Memory.Phase6R.M2.SerializationRoundTrip` + +Observed: + +- `3` focused `Phase 6R-M2` automation tests succeeded + +Non-blocking warnings stayed limited to the existing Unreal headless/editor +noise, expected Python-stub regeneration noise, headless CEF/web-browser +warnings, and benign asset-registry/cache journal chatter during editor +startup. + +## Queue effect + +This packet consumes the current `Phase 6R-M2` implementation slice. + +Memory implementation is now live at the chronicle/continuity layer through +chronicle events, session groups, resume packs, and repo-aware continuity +guards over the landed `6R-M1` ledger substrate. + +If later memory implementation continues: + +- the next clean move is `Phase 6R-M3` +- do not reopen `6R-M2` as one broad "memory packet" diff --git a/docs/arch/HYPERTWIST_PHASE6R_M2_FIRST_PARTY_CHRONICLE_AND_CONTINUITY_PREPARATION_PACKET_2026-05-28.md b/docs/arch/HYPERTWIST_PHASE6R_M2_FIRST_PARTY_CHRONICLE_AND_CONTINUITY_PREPARATION_PACKET_2026-05-28.md new file mode 100644 index 0000000..ce8f4b3 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_M2_FIRST_PARTY_CHRONICLE_AND_CONTINUITY_PREPARATION_PACKET_2026-05-28.md @@ -0,0 +1,115 @@ +# HyperTwist Phase 6R-M2 first-party chronicle and continuity preparation packet + +Created on `2026-05-28` + +## Status + +- first-party HyperTwist packet +- source-backed `Phase 6R-M2` preparation/control slice + +## Purpose + +This packet scopes the second bounded first-party memory implementation slice +under the canonical `Memory Lanes` doctrine. + +The granted slice is: + +- chronicle ledger +- session grouping +- resume packs +- repo-aware continuity guards + +It is not: + +- a broad memory federation packet +- a recall/history-search packet +- a shared-context widening packet +- a cognitive extraction packet +- a knowledge-promotion or user-notes packet +- a compact reducer/digest packet + +## Current authority basis + +This control pass stands on: + +- `docs/ops/HYPERTWIST_MEMORY_LANE_AUTHORITY_AND_PHASE_IMPLEMENTATION_DOCTRINE_2026-05-21.md` +- `docs/ops/HYPERTWIST_CONTINUITY_LATTICE_AND_CONTEXT_ASSEMBLY_PROFILE_DOCTRINE_2026-05-23.md` +- `docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md` +- `docs/v6_5_deep_manual_pack/HyperTwist/ARCHITECTURE.md` +- `docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md` +- `docs/v6_5_deep_manual_pack/HyperTwist/PROVENANCE_AND_TRUST_MODEL.md` +- `docs/arch/HYPERTWIST_PHASE6R_M1_FIRST_PARTY_MEMORY_CONTRACTS_AND_LEDGER_IMPLEMENTATION_PACKET_2026-05-28.md` +- `docs/VECTORSHELL_MEMORY_SYSTEM_IMPORT_AUTHORITY_FOR_HYPERTWIST_2026-05-25.md` + +The preserved owners do not change here: + +- first-party HyperTwist remains the live owner for current memory-lane + contracts, chronicle grouping, continuity resume, and guard surfaces +- the `Phase 6R-M1` ledger remains the base provenance seam beneath this packet +- current training, replay, coaching, and repository code remains the raw + evidence substrate beneath this packet +- the VectorShell memory doctrine remains the external synchronization + entrypoint and authority map, not a frozen imported implementation + +## Granted family + +The bounded `Phase 6R-M2` slice may land: + +1. chronicle event entries over current run-record, review-plan, and + coach-session-queue evidence +2. grouped first-party continuity/session surfaces for: + - training sessions + - review plans + - coach session queues +3. bounded resume packs above those grouped surfaces +4. repo-aware continuity guards over repository-integrity and lineage breaks +5. sample contract and JSON round-trip coverage for the `6R-M2` state + +The packet must stay out of: + +- explicit remember/recall search ownership +- provenance drill-down or shared-context widening +- cognitive extraction, contradiction handling, or supersession +- curated knowledge-promotion flows +- user-authored notes +- compact reducers, digests, or reduced-context packets + +## Proposed implementation shape + +Land the narrower first-party boundary through: + +- new memory types for: + - chronicle events + - session groups + - resume packs + - continuity guards + - bounded chronicle/continuity state +- a memory-core derivation function over current repository state and the + already-landed `6R-M1` ledger seam +- sample-contract helpers for: + - sample chronicle/continuity derivation + - JSON serialization + - JSON deserialization +- focused automation in: + - `HyperTwistMemoryPhase6RM2ChronicleContinuityContractTest.cpp` + +## Validation target + +Validate with: + +- Unreal build for `UnrealHyperTwistEditor Win64 Development` +- focused automation: + - `HyperTwist.FirstParty.Memory.Phase6R.M2.ChronicleGrouping` + - `HyperTwist.FirstParty.Memory.Phase6R.M2.ContinuityGuards` + - `HyperTwist.FirstParty.Memory.Phase6R.M2.SerializationRoundTrip` + +## Queue effect + +If this packet lands cleanly, HyperTwist memory no longer stops at the +contract/ledger seam. The bounded first-party chronicle and continuity layer +becomes current product truth above `6R-M1`. + +If later memory implementation continues: + +- the next clean move is `Phase 6R-M3` +- do not reopen `6R-M2` as one broad memory packet diff --git a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md index c0dcf0b..44fcbca 100644 --- a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md +++ b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md @@ -224,7 +224,9 @@ When HyperTwist later returns to memory implementation: - the bounded first-party `Phase 6R-M1` memory contracts and ledger packet is now landed in current code -- if memory implementation continues, the next clean move is `Phase 6R-M2` +- the bounded first-party `Phase 6R-M2` chronicle and continuity packet is now + landed in current code +- if memory implementation continues, the next clean move is `Phase 6R-M3` - do not open one broad "memory packet" - follow the lane map and packet order in `C:\HyperTwist\docs\ops\HYPERTWIST_MEMORY_LANE_AUTHORITY_AND_PHASE_IMPLEMENTATION_DOCTRINE_2026-05-21.md` diff --git a/docs/ops/HYPERTWIST_MEMORY_LANE_AUTHORITY_AND_PHASE_IMPLEMENTATION_DOCTRINE_2026-05-21.md b/docs/ops/HYPERTWIST_MEMORY_LANE_AUTHORITY_AND_PHASE_IMPLEMENTATION_DOCTRINE_2026-05-21.md index 575da69..69fd433 100644 --- a/docs/ops/HYPERTWIST_MEMORY_LANE_AUTHORITY_AND_PHASE_IMPLEMENTATION_DOCTRINE_2026-05-21.md +++ b/docs/ops/HYPERTWIST_MEMORY_LANE_AUTHORITY_AND_PHASE_IMPLEMENTATION_DOCTRINE_2026-05-21.md @@ -650,7 +650,12 @@ Current standing: - current live `6R-M1` surfaces cover lane ids, memory entity ids, provenance links, authority flags, feature gates, retention profiles, and bounded ledger serialization -- if memory implementation continues, the next clean move is `6R-M2` +- the bounded first-party `Phase 6R-M2` chronicle and continuity packet is now + landed in current code +- current live `6R-M2` surfaces cover chronicle events, grouped continuity + sessions, bounded resume packs, and repo-aware continuity guards over the + landed `6R-M1` ledger seam +- if memory implementation continues, the next clean move is `6R-M3` ### `6R-M2` - chronicle and continuity packet diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ARCHITECTURE.md b/docs/v6_5_deep_manual_pack/HyperTwist/ARCHITECTURE.md index 3efb19b..19cced1 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ARCHITECTURE.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ARCHITECTURE.md @@ -181,6 +181,8 @@ That means: - bounded `Memory Lanes` - a live first-party `Phase 6R-M1` contract/ledger layer over current memory evidence surfaces +- a live first-party `Phase 6R-M2` chronicle/continuity layer over that landed + ledger seam - explicit provenance - explicit custody - two context-assembly profiles: 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 21353fb..f805b7f 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md @@ -289,6 +289,7 @@ repo. | Session continuity and workspace recall fragments | Implemented now | first-party runtime/training surfaces | Real substrate fragments under the canonical `Memory Lanes`; not yet a full lane implementation. | | Provenance-aware training/replay/publication state | Implemented now | first-party contract/provenance surfaces | Existing product truth. | | First-party memory contracts and ledger | Implemented now | landed first-party `Phase 6R-M1` packet | Current bounded memory lane ids, entity ids, provenance links, authority flags, feature gates, retention profiles, and ledger JSON round-trip are live. Chronicle/resume, recall/shared context, cognitive extraction, knowledge/notes, and compact reducers stay deferred to later memory packets. | +| First-party chronicle and continuity | Implemented now | landed first-party `Phase 6R-M2` packet | Current bounded chronicle events, grouped continuity sessions, resume packs, and repo-aware continuity guards are live above the landed `Phase 6R-M1` ledger seam. Recall/history search, shared-context widening, cognitive extraction, knowledge/notes, and compact reducers stay deferred to later memory packets. | | Control-plane telemetry, replay-diagnostic, and rollout-governance reference grounding | Implemented now | `PostHog/posthog` retained boundary-sensitive lane + first-party current code | Current live `Control-Plane Telemetry and Replay Governance` reference side includes six rewritten first-party contract/reference targets grounded in `PostHog/posthog`: replay diagnostics, replay segmentation, feature governance, scheduled changes, early-access lifecycle, and explicit `MIT`-outside-`ee/` compliance-boundary notes. This does not displace the landed `Phase 4R-D` first-party owner lane, any replay-shell owner family, or the explicit enterprise-subtree exclusion boundary. | | Capture-history, vault, and replay-inspection reference grounding | Implemented now | `screenpipe/screenpipe` retained boundary-sensitive lane + first-party current code | Current live `Capture History, Replay, and Vault Support` reference side includes six rewritten first-party contract/reference targets grounded in `screenpipe/screenpipe`: event-capture, pipe-permission, persistence, vault-lifecycle, timeline-review, and explicit permissive-core-versus-`ee/` compliance-boundary notes. This does not displace the landed `Phase 4R-E` first-party owner lane, the broader replay shell, or the explicit enterprise-subtree exclusion boundary. | | Layered memory federation | Deep-source grounded retained | memory doctrine + VectorShell import entrypoint | Governing taxonomy exists; HyperTwist uses pointer-based synchronization to the current VectorShell memory canon rather than a frozen local fork. Lane widening remains future work and later superior evidence may revise only the exact affected lane or sub-slice. | diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/PROVENANCE_AND_TRUST_MODEL.md b/docs/v6_5_deep_manual_pack/HyperTwist/PROVENANCE_AND_TRUST_MODEL.md index 506428e..be50314 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/PROVENANCE_AND_TRUST_MODEL.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/PROVENANCE_AND_TRUST_MODEL.md @@ -97,6 +97,8 @@ and requires provenance to survive: - the landed bounded first-party `Phase 6R-M1` memory contracts and ledger packet +- the landed bounded first-party `Phase 6R-M2` chronicle and continuity + packet - chronicle capture - continuity/resume packets - cognitive coach-state consolidation diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md index 804ba65..0b82be8 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -135,8 +135,12 @@ Canonical discovery surfaces for roadmap interpretation: consumed - the bounded first-party `Phase 6R-M1` memory contracts and ledger packet is now landed in current code -- if memory implementation continues, the next clean move is `Phase 6R-M2` chronicle and - continuity rather than reopening `6R-M1` as a broad memory packet +- the generic first-party `Phase 6R-M2` chronicle and continuity control pass is now + consumed +- the bounded first-party `Phase 6R-M2` chronicle and continuity packet is now landed in + current code +- if memory implementation continues, the next clean move is `Phase 6R-M3` recall and + shared-context rather than reopening `6R-M1` or `6R-M2` as broad memory packets - the generic source-backed `Phase 6R-R` shared classic-cube recognition multi-face correction/explanation control pass is now consumed - the bounded permissive `Phase 6R-R` shared classic-cube recognition multi-face