Implement Phase S1-B skill settings menu and off-state control
This commit is contained in:
parent
87676bdb41
commit
f01fa43af6
15 changed files with 1219 additions and 16 deletions
|
|
@ -26,6 +26,24 @@ namespace HyperTwistContractLibraryInternal
|
|||
return FJsonObjectConverter::JsonObjectStringToUStruct(Json, &OutValue, 0, 0);
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlProfile MakeSampleSkillControlProfile(const bool bMasterEnabled)
|
||||
{
|
||||
FHyperTwistSkillControlProfile ControlProfile;
|
||||
ControlProfile.ProfileId = bMasterEnabled
|
||||
? TEXT("skill-controls/default")
|
||||
: TEXT("skill-controls/all-skills-off");
|
||||
ControlProfile.DisplayLabel = bMasterEnabled
|
||||
? TEXT("Default Skill Controls")
|
||||
: TEXT("All Skills Off");
|
||||
ControlProfile.bMasterEnabled = bMasterEnabled;
|
||||
ControlProfile.bDurableSettingsExposed = true;
|
||||
ControlProfile.bImmediateMenuControlExposed = true;
|
||||
ControlProfile.Summary = bMasterEnabled
|
||||
? TEXT("Default first-party skill control profile.")
|
||||
: TEXT("Control profile proving the core product remains valid with all skills off.");
|
||||
return ControlProfile;
|
||||
}
|
||||
|
||||
FHyperTwistCoachSessionQueueSummary MakeSampleCoachSessionQueueSummaryManual()
|
||||
{
|
||||
FHyperTwistCoachSessionQueueSummary QueueSummary;
|
||||
|
|
@ -3915,6 +3933,22 @@ FHyperTwistSkillRegistryState UHyperTwistContractLibrary::MakeSampleSkillRegistr
|
|||
return UHyperTwistSkillCoreLibrary::DeriveSkillRegistryState();
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlState UHyperTwistContractLibrary::MakeSampleSkillControlState()
|
||||
{
|
||||
return UHyperTwistSkillCoreLibrary::DeriveSkillControlState(
|
||||
MakeSampleSkillRegistryState(),
|
||||
HyperTwistContractLibraryInternal::MakeSampleSkillControlProfile(true)
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlState UHyperTwistContractLibrary::MakeAllSkillsOffSkillControlState()
|
||||
{
|
||||
return UHyperTwistSkillCoreLibrary::DeriveSkillControlState(
|
||||
MakeSampleSkillRegistryState(),
|
||||
HyperTwistContractLibraryInternal::MakeSampleSkillControlProfile(false)
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistContractLibrary::MakeSampleTrainingRepositoryIntegrityReport()
|
||||
{
|
||||
FHyperTwistTrainingRepositoryState RepositoryState = MakeSampleTrainingRepositoryState();
|
||||
|
|
@ -5033,6 +5067,15 @@ FString UHyperTwistContractLibrary::SerializeSkillRegistryStateToJson(
|
|||
);
|
||||
}
|
||||
|
||||
FString UHyperTwistContractLibrary::SerializeSkillControlStateToJson(
|
||||
const FHyperTwistSkillControlState& SkillControlState
|
||||
)
|
||||
{
|
||||
return HyperTwistContractLibraryInternal::SerializeStructToJson(
|
||||
SkillControlState
|
||||
);
|
||||
}
|
||||
|
||||
FString UHyperTwistContractLibrary::SerializeTrainingTimerExportPacketToJson(
|
||||
const FHyperTwistTrainingTimerExportPacket& TimerExportPacket
|
||||
)
|
||||
|
|
@ -5166,6 +5209,17 @@ bool UHyperTwistContractLibrary::DeserializeSkillRegistryStateFromJson(
|
|||
);
|
||||
}
|
||||
|
||||
bool UHyperTwistContractLibrary::DeserializeSkillControlStateFromJson(
|
||||
const FString& Json,
|
||||
FHyperTwistSkillControlState& OutSkillControlState
|
||||
)
|
||||
{
|
||||
return HyperTwistContractLibraryInternal::DeserializeStructFromJson(
|
||||
Json,
|
||||
OutSkillControlState
|
||||
);
|
||||
}
|
||||
|
||||
bool UHyperTwistContractLibrary::DeserializeTrainingTimerExportPacketFromJson(
|
||||
const FString& Json,
|
||||
FHyperTwistTrainingTimerExportPacket& OutTimerExportPacket
|
||||
|
|
|
|||
|
|
@ -120,6 +120,197 @@ namespace HyperTwistSkillCoreLibraryInternal
|
|||
FamilyCount.Count = Count;
|
||||
return FamilyCount;
|
||||
}
|
||||
|
||||
FString MakeSkillSettingsSlug(const FString& SkillId)
|
||||
{
|
||||
FString Slug = SkillId;
|
||||
Slug.ReplaceInline(TEXT("/"), TEXT("."));
|
||||
Slug.ReplaceInline(TEXT("-"), TEXT("."));
|
||||
return Slug;
|
||||
}
|
||||
|
||||
FString MakeEnableSettingsKey(const FString& SkillId)
|
||||
{
|
||||
return TEXT("skills.enabled.") + MakeSkillSettingsSlug(SkillId);
|
||||
}
|
||||
|
||||
FString MakeVisibilitySettingsKey(const FString& SkillId)
|
||||
{
|
||||
return TEXT("skills.visible.") + MakeSkillSettingsSlug(SkillId);
|
||||
}
|
||||
|
||||
FString MakeVisibilityGroupId(const EHyperTwistSkillFamily Family)
|
||||
{
|
||||
switch (Family)
|
||||
{
|
||||
case EHyperTwistSkillFamily::SubstrateGovernance:
|
||||
return TEXT("skill-group/substrate-governance");
|
||||
case EHyperTwistSkillFamily::AnalysisReview:
|
||||
return TEXT("skill-group/analysis-review");
|
||||
case EHyperTwistSkillFamily::MemoryContinuity:
|
||||
return TEXT("skill-group/memory-continuity");
|
||||
case EHyperTwistSkillFamily::ProviderOperations:
|
||||
return TEXT("skill-group/provider-operations");
|
||||
case EHyperTwistSkillFamily::TrainingCoaching:
|
||||
return TEXT("skill-group/training-coaching");
|
||||
case EHyperTwistSkillFamily::DomainCreative:
|
||||
return TEXT("skill-group/domain-creative");
|
||||
default:
|
||||
return FString();
|
||||
}
|
||||
}
|
||||
|
||||
FString MakeVisibilityGroupLabel(const EHyperTwistSkillFamily Family)
|
||||
{
|
||||
switch (Family)
|
||||
{
|
||||
case EHyperTwistSkillFamily::SubstrateGovernance:
|
||||
return TEXT("Substrate Governance");
|
||||
case EHyperTwistSkillFamily::AnalysisReview:
|
||||
return TEXT("Analysis And Review");
|
||||
case EHyperTwistSkillFamily::MemoryContinuity:
|
||||
return TEXT("Memory And Continuity");
|
||||
case EHyperTwistSkillFamily::ProviderOperations:
|
||||
return TEXT("Provider And Operations");
|
||||
case EHyperTwistSkillFamily::TrainingCoaching:
|
||||
return TEXT("Training And Coaching");
|
||||
case EHyperTwistSkillFamily::DomainCreative:
|
||||
return TEXT("Domain And Creative");
|
||||
default:
|
||||
return FString();
|
||||
}
|
||||
}
|
||||
|
||||
FString MakeVisibilityGroupSettingsKey(const EHyperTwistSkillFamily Family)
|
||||
{
|
||||
return TEXT("skills.group.visible.")
|
||||
+ MakeSkillSettingsSlug(MakeVisibilityGroupId(Family));
|
||||
}
|
||||
|
||||
const FHyperTwistSkillControlOverride* FindControlOverride(
|
||||
const FHyperTwistSkillControlProfile& ControlProfile,
|
||||
const FString& SkillId
|
||||
)
|
||||
{
|
||||
for (const FHyperTwistSkillControlOverride& Override : ControlProfile.Overrides)
|
||||
{
|
||||
if (Override.SkillId == SkillId)
|
||||
{
|
||||
return &Override;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FHyperTwistSkillMasterSwitchState MakeMasterSwitchState(
|
||||
const FHyperTwistSkillControlProfile& ControlProfile
|
||||
)
|
||||
{
|
||||
FHyperTwistSkillMasterSwitchState MasterSwitch;
|
||||
MasterSwitch.SettingsKey = TEXT("skills.master.enabled");
|
||||
MasterSwitch.DisplayLabel = TEXT("Enable Skills");
|
||||
MasterSwitch.bEnabled = ControlProfile.bMasterEnabled;
|
||||
MasterSwitch.bDurableSettingExposed = ControlProfile.bDurableSettingsExposed;
|
||||
MasterSwitch.bImmediateMenuControlExposed = ControlProfile.bImmediateMenuControlExposed;
|
||||
MasterSwitch.bAllSkillsOffSafe = true;
|
||||
MasterSwitch.Summary =
|
||||
TEXT("Global master gate for optional assistive skills.");
|
||||
return MasterSwitch;
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlStateEntry MakeControlStateEntry(
|
||||
const FHyperTwistSkillManifestEntry& Entry,
|
||||
const FHyperTwistSkillControlProfile& ControlProfile
|
||||
)
|
||||
{
|
||||
const FHyperTwistSkillControlOverride* Override =
|
||||
FindControlOverride(ControlProfile, Entry.SkillId);
|
||||
const bool bRequestedEnabled =
|
||||
(Override != nullptr && Override->bHasEnabledOverride)
|
||||
? Override->bEnabled
|
||||
: Entry.bEnabledByDefault;
|
||||
const bool bRequestedVisible =
|
||||
(Override != nullptr && Override->bHasVisibilityOverride)
|
||||
? Override->bVisible
|
||||
: Entry.bVisibleByDefault;
|
||||
|
||||
FHyperTwistSkillControlStateEntry SkillState;
|
||||
SkillState.SkillId = Entry.SkillId;
|
||||
SkillState.DisplayLabel = Entry.DisplayLabel;
|
||||
SkillState.EnableSettingsKey = MakeEnableSettingsKey(Entry.SkillId);
|
||||
SkillState.VisibilitySettingsKey = MakeVisibilitySettingsKey(Entry.SkillId);
|
||||
SkillState.VisibilityGroupId = MakeVisibilityGroupId(Entry.Family);
|
||||
SkillState.Family = Entry.Family;
|
||||
SkillState.Status = Entry.Status;
|
||||
SkillState.bInstalled = Entry.bInstalledWhenDisabled;
|
||||
SkillState.bOptionalAssistive = Entry.bOptionalAssistive;
|
||||
SkillState.bRequestedEnabled = bRequestedEnabled;
|
||||
SkillState.bEffectiveEnabled = ControlProfile.bMasterEnabled && bRequestedEnabled;
|
||||
SkillState.bRequestedVisible = bRequestedVisible;
|
||||
SkillState.bEffectiveVisible = bRequestedVisible;
|
||||
SkillState.bInertWhenDisabled = Entry.bInertWhenDisabled;
|
||||
SkillState.bDurableSettingExposed = ControlProfile.bDurableSettingsExposed;
|
||||
SkillState.bImmediateMenuControlExposed = ControlProfile.bImmediateMenuControlExposed;
|
||||
SkillState.Summary = Entry.Summary;
|
||||
return SkillState;
|
||||
}
|
||||
|
||||
void AddOrUpdateVisibilityGroup(
|
||||
FHyperTwistSkillControlState& ControlState,
|
||||
const FHyperTwistSkillControlStateEntry& SkillState
|
||||
)
|
||||
{
|
||||
FHyperTwistSkillVisibilityGroupState* Group = ControlState.VisibilityGroups.FindByPredicate(
|
||||
[&SkillState](const FHyperTwistSkillVisibilityGroupState& ExistingGroup)
|
||||
{
|
||||
return ExistingGroup.GroupId == SkillState.VisibilityGroupId;
|
||||
}
|
||||
);
|
||||
|
||||
if (Group == nullptr)
|
||||
{
|
||||
FHyperTwistSkillVisibilityGroupState NewGroup;
|
||||
NewGroup.GroupId = SkillState.VisibilityGroupId;
|
||||
NewGroup.DisplayLabel = MakeVisibilityGroupLabel(SkillState.Family);
|
||||
NewGroup.VisibilitySettingsKey = MakeVisibilityGroupSettingsKey(SkillState.Family);
|
||||
NewGroup.Family = SkillState.Family;
|
||||
NewGroup.bGroupVisibleByDefault = SkillState.bRequestedVisible;
|
||||
NewGroup.bDurableVisibilityControlExposed = SkillState.bDurableSettingExposed;
|
||||
NewGroup.Summary = TEXT("Family-based visibility grouping for optional skills.");
|
||||
ControlState.VisibilityGroups.Add(NewGroup);
|
||||
Group = &ControlState.VisibilityGroups.Last();
|
||||
}
|
||||
|
||||
Group->SkillIds.Add(SkillState.SkillId);
|
||||
Group->VisibleSkillCount += SkillState.bEffectiveVisible ? 1 : 0;
|
||||
Group->HiddenSkillCount += SkillState.bEffectiveVisible ? 0 : 1;
|
||||
Group->bGroupVisibleByDefault |= SkillState.bRequestedVisible;
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlProfile MakeDefaultControlProfile()
|
||||
{
|
||||
FHyperTwistSkillControlProfile ControlProfile;
|
||||
ControlProfile.ProfileId = TEXT("skill-controls/default");
|
||||
ControlProfile.DisplayLabel = TEXT("Default Skill Controls");
|
||||
ControlProfile.bMasterEnabled = true;
|
||||
ControlProfile.bDurableSettingsExposed = true;
|
||||
ControlProfile.bImmediateMenuControlExposed = true;
|
||||
ControlProfile.Summary =
|
||||
TEXT("Default first-party skill control profile.");
|
||||
return ControlProfile;
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlProfile MakeAllSkillsOffControlProfile()
|
||||
{
|
||||
FHyperTwistSkillControlProfile ControlProfile = MakeDefaultControlProfile();
|
||||
ControlProfile.ProfileId = TEXT("skill-controls/all-skills-off");
|
||||
ControlProfile.DisplayLabel = TEXT("All Skills Off");
|
||||
ControlProfile.bMasterEnabled = false;
|
||||
ControlProfile.Summary =
|
||||
TEXT("Control profile proving the core product remains valid with all skills off.");
|
||||
return ControlProfile;
|
||||
}
|
||||
}
|
||||
|
||||
FHyperTwistSkillRegistryState UHyperTwistSkillCoreLibrary::DeriveSkillRegistryState()
|
||||
|
|
@ -143,7 +334,7 @@ FHyperTwistSkillRegistryState UHyperTwistSkillCoreLibrary::DeriveSkillRegistrySt
|
|||
EHyperTwistSkillFamily::SubstrateGovernance,
|
||||
EHyperTwistSkillStatus::ImplementedNow,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
{
|
||||
MakeCommandBinding(
|
||||
TEXT("command/skills/inspect-registry"),
|
||||
|
|
@ -184,7 +375,7 @@ FHyperTwistSkillRegistryState UHyperTwistSkillCoreLibrary::DeriveSkillRegistrySt
|
|||
EHyperTwistSkillFamily::AnalysisReview,
|
||||
EHyperTwistSkillStatus::CommandContractPending,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
{
|
||||
MakeCommandBinding(
|
||||
TEXT("command/skills/review-architecture"),
|
||||
|
|
@ -225,7 +416,7 @@ FHyperTwistSkillRegistryState UHyperTwistSkillCoreLibrary::DeriveSkillRegistrySt
|
|||
EHyperTwistSkillFamily::MemoryContinuity,
|
||||
EHyperTwistSkillStatus::CommandContractPending,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
{
|
||||
MakeCommandBinding(
|
||||
TEXT("command/skills/memory-resume-briefing"),
|
||||
|
|
@ -266,7 +457,7 @@ FHyperTwistSkillRegistryState UHyperTwistSkillCoreLibrary::DeriveSkillRegistrySt
|
|||
EHyperTwistSkillFamily::ProviderOperations,
|
||||
EHyperTwistSkillStatus::CommandContractPending,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
{
|
||||
MakeCommandBinding(
|
||||
TEXT("command/skills/provider-routing-inspector"),
|
||||
|
|
@ -413,3 +604,52 @@ FHyperTwistSkillRegistryState UHyperTwistSkillCoreLibrary::DeriveSkillRegistrySt
|
|||
|
||||
return RegistryState;
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlState UHyperTwistSkillCoreLibrary::DeriveSkillControlState(
|
||||
const FHyperTwistSkillRegistryState& RegistryState,
|
||||
const FHyperTwistSkillControlProfile& ControlProfile
|
||||
)
|
||||
{
|
||||
using namespace HyperTwistSkillCoreLibraryInternal;
|
||||
|
||||
if (!RegistryState.IsStructurallyValid() || !ControlProfile.IsStructurallyValid())
|
||||
{
|
||||
return FHyperTwistSkillControlState();
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlState ControlState;
|
||||
ControlState.RegistryId = RegistryState.RegistryId;
|
||||
ControlState.ManifestVersion = TEXT("s1b-v1");
|
||||
ControlState.ReferenceUtc = TEXT("2026-05-28T21:15:00Z");
|
||||
ControlState.CommandSurfaceRootId = RegistryState.CommandSurfaceRootId;
|
||||
ControlState.ControlProfileId = ControlProfile.ProfileId;
|
||||
ControlState.MasterSwitch = MakeMasterSwitchState(ControlProfile);
|
||||
ControlState.Summary =
|
||||
TEXT("First-party skill settings, menu, and off-state control contract.");
|
||||
|
||||
for (const FHyperTwistSkillManifestEntry& Entry : RegistryState.Entries)
|
||||
{
|
||||
const FHyperTwistSkillControlStateEntry SkillState =
|
||||
MakeControlStateEntry(Entry, ControlProfile);
|
||||
if (!SkillState.IsStructurallyValid())
|
||||
{
|
||||
return FHyperTwistSkillControlState();
|
||||
}
|
||||
|
||||
ControlState.SkillStates.Add(SkillState);
|
||||
ControlState.EffectiveEnabledSkillCount += SkillState.bEffectiveEnabled ? 1 : 0;
|
||||
ControlState.VisibleSkillCount += SkillState.bEffectiveVisible ? 1 : 0;
|
||||
AddOrUpdateVisibilityGroup(ControlState, SkillState);
|
||||
}
|
||||
|
||||
ControlState.SkillCount = ControlState.SkillStates.Num();
|
||||
ControlState.EffectiveDisabledSkillCount =
|
||||
ControlState.SkillCount - ControlState.EffectiveEnabledSkillCount;
|
||||
ControlState.HiddenSkillCount =
|
||||
ControlState.SkillCount - ControlState.VisibleSkillCount;
|
||||
ControlState.bCoreProductWorksWithAllSkillsOff = true;
|
||||
ControlState.bNoPromptBabysittingToDisable = true;
|
||||
ControlState.bAllInstalledSkillsRemainReenableable = true;
|
||||
|
||||
return ControlState;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,6 +167,12 @@ public:
|
|||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Skills")
|
||||
static FHyperTwistSkillRegistryState MakeSampleSkillRegistryState();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Skills")
|
||||
static FHyperTwistSkillControlState MakeSampleSkillControlState();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Skills")
|
||||
static FHyperTwistSkillControlState MakeAllSkillsOffSkillControlState();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training")
|
||||
static FHyperTwistTrainingRepositoryIntegrityReport MakeSampleTrainingRepositoryIntegrityReport();
|
||||
|
||||
|
|
@ -361,6 +367,11 @@ public:
|
|||
const FHyperTwistSkillRegistryState& SkillRegistryState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Serialization")
|
||||
static FString SerializeSkillControlStateToJson(
|
||||
const FHyperTwistSkillControlState& SkillControlState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Serialization")
|
||||
static FString SerializeTrainingTimerExportPacketToJson(const FHyperTwistTrainingTimerExportPacket& TimerExportPacket);
|
||||
|
||||
|
|
@ -439,6 +450,12 @@ public:
|
|||
FHyperTwistSkillRegistryState& OutSkillRegistryState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Serialization")
|
||||
static bool DeserializeSkillControlStateFromJson(
|
||||
const FString& Json,
|
||||
FHyperTwistSkillControlState& OutSkillControlState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Serialization")
|
||||
static bool DeserializeTrainingTimerExportPacketFromJson(const FString& Json, FHyperTwistTrainingTimerExportPacket& OutTimerExportPacket);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,4 +13,10 @@ class UNREALHYPERTWIST_API UHyperTwistSkillCoreLibrary : public UBlueprintFuncti
|
|||
public:
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Skills")
|
||||
static FHyperTwistSkillRegistryState DeriveSkillRegistryState();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Skills")
|
||||
static FHyperTwistSkillControlState DeriveSkillControlState(
|
||||
const FHyperTwistSkillRegistryState& RegistryState,
|
||||
const FHyperTwistSkillControlProfile& ControlProfile
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -423,3 +423,381 @@ struct FHyperTwistSkillRegistryState
|
|||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillControlOverride
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SkillId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasEnabledOverride = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bEnabled = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasVisibilityOverride = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bVisible = false;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !SkillId.IsEmpty()
|
||||
&& (bHasEnabledOverride || bHasVisibilityOverride);
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillControlProfile
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DisplayLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bMasterEnabled = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bDurableSettingsExposed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bImmediateMenuControlExposed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSkillControlOverride> Overrides;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (ProfileId.IsEmpty()
|
||||
|| DisplayLabel.IsEmpty()
|
||||
|| !bDurableSettingsExposed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<FString> SeenSkillIds;
|
||||
for (const FHyperTwistSkillControlOverride& Override : Overrides)
|
||||
{
|
||||
if (!Override.IsStructurallyValid() || SeenSkillIds.Contains(Override.SkillId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SeenSkillIds.Add(Override.SkillId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillMasterSwitchState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettingsKey;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DisplayLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bEnabled = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bDurableSettingExposed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bImmediateMenuControlExposed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bAllSkillsOffSafe = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !SettingsKey.IsEmpty()
|
||||
&& !DisplayLabel.IsEmpty()
|
||||
&& bDurableSettingExposed
|
||||
&& bAllSkillsOffSafe;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillControlStateEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SkillId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DisplayLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString EnableSettingsKey;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString VisibilitySettingsKey;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString VisibilityGroupId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistSkillFamily Family = EHyperTwistSkillFamily::None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistSkillStatus Status = EHyperTwistSkillStatus::None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bInstalled = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bOptionalAssistive = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequestedEnabled = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bEffectiveEnabled = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequestedVisible = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bEffectiveVisible = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bInertWhenDisabled = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bDurableSettingExposed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bImmediateMenuControlExposed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !SkillId.IsEmpty()
|
||||
&& !DisplayLabel.IsEmpty()
|
||||
&& !EnableSettingsKey.IsEmpty()
|
||||
&& !VisibilitySettingsKey.IsEmpty()
|
||||
&& !VisibilityGroupId.IsEmpty()
|
||||
&& Family != EHyperTwistSkillFamily::None
|
||||
&& Status != EHyperTwistSkillStatus::None
|
||||
&& bInstalled
|
||||
&& bOptionalAssistive
|
||||
&& bInertWhenDisabled
|
||||
&& bDurableSettingExposed
|
||||
&& (!bEffectiveEnabled || bRequestedEnabled);
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillVisibilityGroupState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString GroupId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DisplayLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString VisibilitySettingsKey;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistSkillFamily Family = EHyperTwistSkillFamily::None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> SkillIds;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 VisibleSkillCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 HiddenSkillCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bGroupVisibleByDefault = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bDurableVisibilityControlExposed = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !GroupId.IsEmpty()
|
||||
&& !DisplayLabel.IsEmpty()
|
||||
&& !VisibilitySettingsKey.IsEmpty()
|
||||
&& Family != EHyperTwistSkillFamily::None
|
||||
&& SkillIds.Num() > 0
|
||||
&& VisibleSkillCount >= 0
|
||||
&& HiddenSkillCount >= 0
|
||||
&& (VisibleSkillCount + HiddenSkillCount) == SkillIds.Num()
|
||||
&& bDurableVisibilityControlExposed;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillControlState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString RegistryId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ManifestVersion;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ReferenceUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CommandSurfaceRootId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ControlProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSkillMasterSwitchState MasterSwitch;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSkillControlStateEntry> SkillStates;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSkillVisibilityGroupState> VisibilityGroups;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SkillCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 EffectiveEnabledSkillCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 EffectiveDisabledSkillCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 VisibleSkillCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 HiddenSkillCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCoreProductWorksWithAllSkillsOff = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bNoPromptBabysittingToDisable = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bAllInstalledSkillsRemainReenableable = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (RegistryId.IsEmpty()
|
||||
|| ManifestVersion.IsEmpty()
|
||||
|| ReferenceUtc.IsEmpty()
|
||||
|| CommandSurfaceRootId.IsEmpty()
|
||||
|| ControlProfileId.IsEmpty()
|
||||
|| !MasterSwitch.IsStructurallyValid()
|
||||
|| SkillStates.Num() == 0
|
||||
|| SkillCount != SkillStates.Num()
|
||||
|| EffectiveDisabledSkillCount != (SkillCount - EffectiveEnabledSkillCount)
|
||||
|| HiddenSkillCount != (SkillCount - VisibleSkillCount)
|
||||
|| !bCoreProductWorksWithAllSkillsOff
|
||||
|| !bNoPromptBabysittingToDisable
|
||||
|| !bAllInstalledSkillsRemainReenableable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<FString> SeenSkillIds;
|
||||
int32 ComputedEnabledCount = 0;
|
||||
int32 ComputedVisibleCount = 0;
|
||||
bool bComputedNoBabysitting = true;
|
||||
bool bComputedReenableable = true;
|
||||
for (const FHyperTwistSkillControlStateEntry& SkillState : SkillStates)
|
||||
{
|
||||
if (!SkillState.IsStructurallyValid() || SeenSkillIds.Contains(SkillState.SkillId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SeenSkillIds.Add(SkillState.SkillId);
|
||||
ComputedEnabledCount += SkillState.bEffectiveEnabled ? 1 : 0;
|
||||
ComputedVisibleCount += SkillState.bEffectiveVisible ? 1 : 0;
|
||||
bComputedNoBabysitting &= SkillState.bDurableSettingExposed;
|
||||
bComputedReenableable &= SkillState.bInstalled && SkillState.bInertWhenDisabled;
|
||||
}
|
||||
|
||||
if (ComputedEnabledCount != EffectiveEnabledSkillCount
|
||||
|| ComputedVisibleCount != VisibleSkillCount
|
||||
|| bComputedNoBabysitting != bNoPromptBabysittingToDisable
|
||||
|| bComputedReenableable != bAllInstalledSkillsRemainReenableable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<FString> SeenGroupIds;
|
||||
for (const FHyperTwistSkillVisibilityGroupState& Group : VisibilityGroups)
|
||||
{
|
||||
if (!Group.IsStructurallyValid() || SeenGroupIds.Contains(Group.GroupId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 ComputedGroupVisibleCount = 0;
|
||||
int32 ComputedGroupHiddenCount = 0;
|
||||
for (const FString& SkillId : Group.SkillIds)
|
||||
{
|
||||
const FHyperTwistSkillControlStateEntry* SkillState = SkillStates.FindByPredicate(
|
||||
[&SkillId](const FHyperTwistSkillControlStateEntry& Entry)
|
||||
{
|
||||
return Entry.SkillId == SkillId;
|
||||
}
|
||||
);
|
||||
if (SkillState == nullptr
|
||||
|| SkillState->Family != Group.Family
|
||||
|| SkillState->VisibilityGroupId != Group.GroupId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ComputedGroupVisibleCount += SkillState->bEffectiveVisible ? 1 : 0;
|
||||
ComputedGroupHiddenCount += SkillState->bEffectiveVisible ? 0 : 1;
|
||||
}
|
||||
|
||||
if (ComputedGroupVisibleCount != Group.VisibleSkillCount
|
||||
|| ComputedGroupHiddenCount != Group.HiddenSkillCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SeenGroupIds.Add(Group.GroupId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,276 @@
|
|||
// Copyright HyperTwist, Inc. All Rights Reserved.
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
|
||||
#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"
|
||||
|
||||
#if WITH_AUTOMATION_TESTS
|
||||
|
||||
namespace HyperTwistSkillPhaseS1BTestInternal
|
||||
{
|
||||
const FHyperTwistSkillControlStateEntry* FindSkillStateById(
|
||||
const FHyperTwistSkillControlState& ControlState,
|
||||
const FString& SkillId
|
||||
)
|
||||
{
|
||||
for (const FHyperTwistSkillControlStateEntry& SkillState : ControlState.SkillStates)
|
||||
{
|
||||
if (SkillState.SkillId == SkillId)
|
||||
{
|
||||
return &SkillState;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const FHyperTwistSkillVisibilityGroupState* FindVisibilityGroupById(
|
||||
const FHyperTwistSkillControlState& ControlState,
|
||||
const FString& GroupId
|
||||
)
|
||||
{
|
||||
for (const FHyperTwistSkillVisibilityGroupState& Group : ControlState.VisibilityGroups)
|
||||
{
|
||||
if (Group.GroupId == GroupId)
|
||||
{
|
||||
return &Group;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistSkillPhaseS1BSettingsVisibilityTest,
|
||||
"HyperTwist.FirstParty.Skill.PhaseS1B.SettingsVisibility",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistSkillPhaseS1BSettingsVisibilityTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
const FHyperTwistSkillControlState ControlState =
|
||||
UHyperTwistContractLibrary::MakeSampleSkillControlState();
|
||||
|
||||
TestTrue(
|
||||
TEXT("The Phase S1-B skill-control state must be structurally valid."),
|
||||
ControlState.IsStructurallyValid()
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The master switch must be enabled in the default control profile."),
|
||||
ControlState.MasterSwitch.bEnabled
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The S1-B control state must expose durable settings."),
|
||||
ControlState.MasterSwitch.bDurableSettingExposed
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The S1-B control state must expose immediate menu control."),
|
||||
ControlState.MasterSwitch.bImmediateMenuControlExposed
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The sample S1-B control state must preserve all six installed skills."),
|
||||
ControlState.SkillCount,
|
||||
6
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The default S1-B control state must keep one effective enabled skill."),
|
||||
ControlState.EffectiveEnabledSkillCount,
|
||||
1
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The S1-B control state must keep visible family groupings."),
|
||||
ControlState.VisibilityGroups.Num() > 0
|
||||
);
|
||||
|
||||
const FHyperTwistSkillControlStateEntry* GovernanceState =
|
||||
HyperTwistSkillPhaseS1BTestInternal::FindSkillStateById(
|
||||
ControlState,
|
||||
TEXT("skill/skillization-governance")
|
||||
);
|
||||
TestNotNull(
|
||||
TEXT("The governance skill state must exist."),
|
||||
GovernanceState
|
||||
);
|
||||
if (GovernanceState != nullptr)
|
||||
{
|
||||
TestTrue(
|
||||
TEXT("The governance skill must remain effectively enabled by default."),
|
||||
GovernanceState->bEffectiveEnabled
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The governance skill must remain visible by default."),
|
||||
GovernanceState->bEffectiveVisible
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The governance skill must use the substrate visibility group."),
|
||||
GovernanceState->VisibilityGroupId,
|
||||
TEXT("skill-group/substrate-governance")
|
||||
);
|
||||
}
|
||||
|
||||
const FHyperTwistSkillVisibilityGroupState* GovernanceGroup =
|
||||
HyperTwistSkillPhaseS1BTestInternal::FindVisibilityGroupById(
|
||||
ControlState,
|
||||
TEXT("skill-group/substrate-governance")
|
||||
);
|
||||
TestNotNull(
|
||||
TEXT("The substrate-governance visibility group must exist."),
|
||||
GovernanceGroup
|
||||
);
|
||||
if (GovernanceGroup != nullptr)
|
||||
{
|
||||
TestEqual(
|
||||
TEXT("The governance visibility group must contain one skill."),
|
||||
GovernanceGroup->SkillIds.Num(),
|
||||
1
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The governance visibility group must expose one visible skill."),
|
||||
GovernanceGroup->VisibleSkillCount,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistSkillPhaseS1BAllSkillsOffCorrectnessTest,
|
||||
"HyperTwist.FirstParty.Skill.PhaseS1B.AllSkillsOffCorrectness",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistSkillPhaseS1BAllSkillsOffCorrectnessTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
const FHyperTwistSkillControlState ControlState =
|
||||
UHyperTwistContractLibrary::MakeAllSkillsOffSkillControlState();
|
||||
|
||||
TestTrue(
|
||||
TEXT("The all-skills-off S1-B control state must remain structurally valid."),
|
||||
ControlState.IsStructurallyValid()
|
||||
);
|
||||
TestFalse(
|
||||
TEXT("The master switch must be disabled in the all-skills-off profile."),
|
||||
ControlState.MasterSwitch.bEnabled
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The all-skills-off control state must confirm off-state safety."),
|
||||
ControlState.MasterSwitch.bAllSkillsOffSafe
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The all-skills-off control state must expose zero effective enabled skills."),
|
||||
ControlState.EffectiveEnabledSkillCount,
|
||||
0
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The all-skills-off control state must preserve all installed skills as disabled."),
|
||||
ControlState.EffectiveDisabledSkillCount,
|
||||
ControlState.SkillCount
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The core product must still work with all skills off."),
|
||||
ControlState.bCoreProductWorksWithAllSkillsOff
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("Disabled skills must remain reenableable."),
|
||||
ControlState.bAllInstalledSkillsRemainReenableable
|
||||
);
|
||||
|
||||
const FHyperTwistSkillControlStateEntry* GovernanceState =
|
||||
HyperTwistSkillPhaseS1BTestInternal::FindSkillStateById(
|
||||
ControlState,
|
||||
TEXT("skill/skillization-governance")
|
||||
);
|
||||
TestNotNull(
|
||||
TEXT("The governance skill state must still exist with all skills off."),
|
||||
GovernanceState
|
||||
);
|
||||
if (GovernanceState != nullptr)
|
||||
{
|
||||
TestTrue(
|
||||
TEXT("The governance skill must stay requested-enabled by default."),
|
||||
GovernanceState->bRequestedEnabled
|
||||
);
|
||||
TestFalse(
|
||||
TEXT("The governance skill must become effectively disabled when the master switch is off."),
|
||||
GovernanceState->bEffectiveEnabled
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The governance skill must remain installed when disabled."),
|
||||
GovernanceState->bInstalled
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The governance skill must remain inert when disabled."),
|
||||
GovernanceState->bInertWhenDisabled
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistSkillPhaseS1BSerializationRoundTripTest,
|
||||
"HyperTwist.FirstParty.Skill.PhaseS1B.SerializationRoundTrip",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistSkillPhaseS1BSerializationRoundTripTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
const FHyperTwistSkillControlState ControlState =
|
||||
UHyperTwistContractLibrary::MakeSampleSkillControlState();
|
||||
TestTrue(
|
||||
TEXT("The Phase S1-B skill-control state must be structurally valid before serialization."),
|
||||
ControlState.IsStructurallyValid()
|
||||
);
|
||||
|
||||
const FString Json =
|
||||
UHyperTwistContractLibrary::SerializeSkillControlStateToJson(ControlState);
|
||||
TestFalse(TEXT("The serialized S1-B JSON must not be empty."), Json.IsEmpty());
|
||||
TestTrue(
|
||||
TEXT("The serialized S1-B JSON must expose the master-switch contract."),
|
||||
Json.Contains(TEXT("MasterSwitch"))
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The serialized S1-B JSON must expose visibility groups."),
|
||||
Json.Contains(TEXT("VisibilityGroups"))
|
||||
);
|
||||
|
||||
FHyperTwistSkillControlState RoundTrippedState;
|
||||
TestTrue(
|
||||
TEXT("The S1-B JSON must deserialize cleanly."),
|
||||
UHyperTwistContractLibrary::DeserializeSkillControlStateFromJson(
|
||||
Json,
|
||||
RoundTrippedState
|
||||
)
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("The round-tripped S1-B control state must remain structurally valid."),
|
||||
RoundTrippedState.IsStructurallyValid()
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The round-tripped S1-B control state must preserve skill counts."),
|
||||
RoundTrippedState.SkillCount,
|
||||
ControlState.SkillCount
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The round-tripped S1-B control state must preserve effective enabled counts."),
|
||||
RoundTrippedState.EffectiveEnabledSkillCount,
|
||||
ControlState.EffectiveEnabledSkillCount
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The round-tripped S1-B control state must preserve visibility-group counts."),
|
||||
RoundTrippedState.VisibilityGroups.Num(),
|
||||
ControlState.VisibilityGroups.Num()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
# HyperTwist Phase S1-B first-party skill settings, menu, and off-state control implementation packet
|
||||
|
||||
Created on `2026-05-28`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- bounded `Phase S1-B` implementation slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet lands the bounded first-party skill settings, menu, and off-state
|
||||
control seam under the canonical `Skillization And Command Surface` doctrine.
|
||||
|
||||
The landed slice is:
|
||||
|
||||
- global skill enable/disable master switch
|
||||
- per-skill enable/disable state
|
||||
- visibility grouping
|
||||
|
||||
It is not:
|
||||
|
||||
- invocation/provenance audit ledger execution
|
||||
- permissive analyzer wrappers
|
||||
- restrictive clean-room command-contract specs
|
||||
- wrapper execution or donor command promotion
|
||||
|
||||
## Current authority basis
|
||||
|
||||
This implementation packet stands on:
|
||||
|
||||
- `docs/ops/HYPERTWIST_SKILLIZATION_AND_COMMAND_SURFACE_DOCTRINE_2026-05-21.md`
|
||||
- `docs/ops/HYPERTWIST_OPTIONAL_ASSISTIVE_FEATURE_DEACTIVATION_AND_REMOVABILITY_DOCTRINE_2026-05-21.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/SKILLS.md`
|
||||
- `docs/v6_5_deep_manual_pack/HyperTwist/PROVENANCE_AND_TRUST_MODEL.md`
|
||||
- `docs/arch/HYPERTWIST_PHASES1_B_FIRST_PARTY_SKILL_SETTINGS_MENU_AND_OFF_STATE_CONTROL_PREPARATION_PACKET_2026-05-28.md`
|
||||
|
||||
The preserved owners do not change:
|
||||
|
||||
- first-party HyperTwist remains the owner of the product command surface
|
||||
- the landed `Phase S1-A` registry remains the current authoritative substrate
|
||||
beneath this packet
|
||||
- optional assistive skills remain product-owned adjunct declarations rather
|
||||
than donor command truth
|
||||
|
||||
## Landed scope
|
||||
|
||||
The current code now owns a bounded first-party skill-control seam through:
|
||||
|
||||
- canonical skill control-profile, master-switch, per-skill control-state,
|
||||
visibility-group, and aggregate control-state types in:
|
||||
- `HyperTwistSkillTypes.h`
|
||||
- first-party control-state derivation over the landed `S1-A` registry in:
|
||||
- `UHyperTwistSkillCoreLibrary`
|
||||
- current bounded control coverage for:
|
||||
- durable global skill master-switch declaration
|
||||
- per-skill durable enable/disable settings keys
|
||||
- per-skill requested/effective enable state
|
||||
- per-skill requested/effective visibility state
|
||||
- family-based visibility groups
|
||||
- immediate menu-control exposure declarations
|
||||
- all-skills-off correctness
|
||||
- sample contract helpers in:
|
||||
- `UHyperTwistContractLibrary::MakeSampleSkillControlState()`
|
||||
- `UHyperTwistContractLibrary::MakeAllSkillsOffSkillControlState()`
|
||||
- `UHyperTwistContractLibrary::SerializeSkillControlStateToJson(...)`
|
||||
- `UHyperTwistContractLibrary::DeserializeSkillControlStateFromJson(...)`
|
||||
- focused automation coverage in:
|
||||
- `HyperTwistSkillPhaseS1BSettingsVisibilityContractTest.cpp`
|
||||
|
||||
## Why this is still intentionally bounded
|
||||
|
||||
This packet lands control-state and visibility-group contracts only.
|
||||
|
||||
Still deferred:
|
||||
|
||||
- invocation/provenance audit ledger execution
|
||||
- permissive analyzer wrappers
|
||||
- restrictive clean-room command-contract specs
|
||||
|
||||
## Validation
|
||||
|
||||
Build validation:
|
||||
|
||||
- `UnrealHyperTwistEditor Win64 Development`
|
||||
|
||||
Focused automation validation:
|
||||
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1B.SettingsVisibility`
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1B.AllSkillsOffCorrectness`
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1B.SerializationRoundTrip`
|
||||
|
||||
Observed:
|
||||
|
||||
- `3` focused `Phase S1-B` automation tests succeeded
|
||||
|
||||
## Queue effect
|
||||
|
||||
This packet consumes the current `Phase S1-B` implementation slice.
|
||||
|
||||
After this landing:
|
||||
|
||||
- `S1-B` is landed in current code
|
||||
- `S1-C` is the next clean skillization move
|
||||
- do not jump straight from `S1-B` to analyzer wrappers or restrictive command
|
||||
specs
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
# HyperTwist Phase S1-B first-party skill settings, menu, and off-state control preparation packet
|
||||
|
||||
Created on `2026-05-28`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- source-backed `Phase S1-B` preparation/control slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet scopes the second bounded product implementation slice under the
|
||||
canonical `Skillization And Command Surface` doctrine.
|
||||
|
||||
The granted slice is:
|
||||
|
||||
- global skill enable/disable master switch
|
||||
- per-skill enable/disable state
|
||||
- visibility grouping
|
||||
|
||||
It is not:
|
||||
|
||||
- invocation/provenance audit ledger execution
|
||||
- permissive analyzer wrappers
|
||||
- restrictive clean-room command-contract specs
|
||||
- wrapper execution or donor command promotion
|
||||
|
||||
## Current authority basis
|
||||
|
||||
This control pass stands on:
|
||||
|
||||
- `docs/ops/HYPERTWIST_SKILLIZATION_AND_COMMAND_SURFACE_DOCTRINE_2026-05-21.md`
|
||||
- `docs/ops/HYPERTWIST_OPTIONAL_ASSISTIVE_FEATURE_DEACTIVATION_AND_REMOVABILITY_DOCTRINE_2026-05-21.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/SKILLS.md`
|
||||
- `docs/v6_5_deep_manual_pack/HyperTwist/PROVENANCE_AND_TRUST_MODEL.md`
|
||||
- `docs/arch/HYPERTWIST_PHASES1_A_FIRST_PARTY_SKILL_REGISTRY_AND_MANIFEST_CONTRACT_IMPLEMENTATION_PACKET_2026-05-28.md`
|
||||
|
||||
The preserved owners do not change here:
|
||||
|
||||
- first-party HyperTwist remains the owner of the product command surface
|
||||
- the landed `Phase S1-A` registry remains the authoritative substrate
|
||||
beneath this packet
|
||||
- optional assistive skills remain product-owned adjuncts rather than donor
|
||||
command truth
|
||||
|
||||
## Granted family
|
||||
|
||||
The bounded `Phase S1-B` slice may land:
|
||||
|
||||
1. first-party skill control profile and master-switch state
|
||||
2. first-party per-skill requested/effective enable state
|
||||
3. first-party per-skill requested/effective visibility state
|
||||
4. family-based visibility grouping
|
||||
5. explicit all-skills-off correctness
|
||||
6. sample control-state and JSON round-trip coverage
|
||||
|
||||
The packet must stay out of:
|
||||
|
||||
- invocation record storage
|
||||
- service/wrapper execution
|
||||
- restrictive clean-room command specs
|
||||
|
||||
## Proposed implementation shape
|
||||
|
||||
Land the narrower first-party boundary through:
|
||||
|
||||
- new skill types for:
|
||||
- control profile
|
||||
- master switch
|
||||
- per-skill control-state entries
|
||||
- visibility groups
|
||||
- bounded control-state aggregate
|
||||
- a first-party skill-core derivation function over the landed `S1-A` registry
|
||||
- sample-contract helpers for:
|
||||
- default control-state derivation
|
||||
- all-skills-off derivation
|
||||
- JSON serialization
|
||||
- JSON deserialization
|
||||
- focused automation in:
|
||||
- `HyperTwistSkillPhaseS1BSettingsVisibilityContractTest.cpp`
|
||||
|
||||
## Validation target
|
||||
|
||||
Validate with:
|
||||
|
||||
- Unreal build for `UnrealHyperTwistEditor Win64 Development`
|
||||
- focused automation:
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1B.SettingsVisibility`
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1B.AllSkillsOffCorrectness`
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1B.SerializationRoundTrip`
|
||||
|
||||
## Queue effect
|
||||
|
||||
If this packet lands cleanly:
|
||||
|
||||
- `S1-B` is consumed
|
||||
- `S1-C` becomes the next clean move
|
||||
- do not jump straight from `S1-B` to analyzer wrappers or restrictive specs
|
||||
|
|
@ -273,6 +273,9 @@ Specifically:
|
|||
|
||||
- the bounded first-party `Phase S1-A` skill registry and manifest contract
|
||||
packet is now landed in current code
|
||||
- `S1-B` settings/menu/off-state control is the next clean move
|
||||
- `S2-A` permissive analyzer wrappers stay deferred until `S1-B` lands
|
||||
- the bounded first-party `Phase S1-B` settings/menu/off-state control packet
|
||||
is now landed in current code
|
||||
- `S1-C` invocation/provenance/audit ledger is the next clean move
|
||||
- `S1-D` authoring/examples/validation-harness expansion stays after `S1-C`
|
||||
- `S2-A` permissive analyzer wrappers stay deferred until `S1-D` lands
|
||||
- `S2-B` clean-room command-contract specs stay deferred until `S2-A`
|
||||
|
|
|
|||
|
|
@ -220,6 +220,13 @@ Acceptance gates:
|
|||
- core product still works with all skills off
|
||||
- no skill requires prompt babysitting to disable
|
||||
|
||||
Current state:
|
||||
|
||||
- the bounded first-party `Phase S1-B` settings/menu/off-state control packet
|
||||
is now landed in current code
|
||||
- invocation/provenance audit ledger execution remains deferred to `S1-C`
|
||||
- analyzer wrappers remain deferred to `S2-A`
|
||||
|
||||
#### `S1-C` invocation, provenance, and audit ledger
|
||||
|
||||
Outputs:
|
||||
|
|
@ -457,9 +464,10 @@ These are target-shape command families, not claims of implemented reality.
|
|||
|
||||
The next correct sequence is:
|
||||
|
||||
1. `S1-B` per-skill settings/menu exposure and off-state behavior
|
||||
2. `S2-A` permissive analyzer wrappers
|
||||
3. `S2-B` clean-room command-contract specs for restrictive lanes
|
||||
1. `S1-C` invocation, provenance, and audit ledger
|
||||
2. `S1-D` authoring, examples, and validation harness
|
||||
3. `S2-A` permissive analyzer wrappers
|
||||
4. `S2-B` clean-room command-contract specs for restrictive lanes
|
||||
|
||||
Broader memory, browser, workflow, provider, and domain skill widening should
|
||||
wait until that base exists.
|
||||
|
|
|
|||
|
|
@ -237,4 +237,6 @@ Future skill growth must follow:
|
|||
- the bounded first-party `Phase S1-A` registry/manifest seam is now the live
|
||||
product base for skill status, command binding, permission, and provenance
|
||||
declarations
|
||||
- `S1-B` remains the next clean move before any wrapper widening
|
||||
- the bounded first-party `Phase S1-B` control seam is now the live product
|
||||
base for durable off-state behavior and family visibility grouping
|
||||
- `S1-C` remains the next clean move before any wrapper widening
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ repo.
|
|||
|
||||
| Feature | Status | Primary authority | Notes |
|
||||
|---|---|---|---|
|
||||
| First-party skill registry and enable/disable governance | Implemented now | landed first-party `Phase S1-A` packet | Current bounded first-party skill status/family contracts, command/service binding declarations, permission/scope declarations, provenance/log declarations, manifest entries, registry counts, and disabled-but-installed inert-off-state truth are live. Settings/menu controls and invocation ledger execution remain deferred to later bounded skillization stages. |
|
||||
| First-party skill registry and enable/disable governance | Implemented now | landed first-party `Phase S1-A` and `Phase S1-B` packets | Current bounded first-party skill status/family contracts, command/service binding declarations, permission/scope declarations, provenance/log declarations, manifest entries, registry counts, durable master-switch/per-skill control-state, family visibility groups, and disabled-but-installed inert-off-state truth are live. Invocation ledger execution remains deferred to later bounded skillization stages. |
|
||||
| Review/analyzer skill family | Deep-source grounded retained | skillization doctrine | Planned after substrate and off-state controls. |
|
||||
| Memory/continuity skill family | Deep-source grounded retained | skillization + memory doctrine | Retained, not implemented. |
|
||||
| Provider/ops skill family | Deep-source grounded retained | skillization + provider doctrine | Retained, not implemented. |
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ The landed bounded first-party `Phase S1-A` packet keeps:
|
|||
- command/service bindings declared rather than inferred ad hoc
|
||||
- permission/scope declarations explicit
|
||||
- provenance/log expectations declared before later invocation-ledger storage
|
||||
- the landed bounded first-party `Phase S1-B` packet keeps durable master and
|
||||
per-skill off-state control first-party rather than hidden prompt babysitting
|
||||
|
||||
## Memory-specific consequence
|
||||
|
||||
|
|
|
|||
|
|
@ -161,7 +161,11 @@ Canonical discovery surfaces for roadmap interpretation:
|
|||
control pass is now consumed
|
||||
- the bounded first-party `Phase S1-A` skill registry and manifest contract
|
||||
packet is now landed in current code
|
||||
- `S1-B` settings/menu/off-state control is now the next clean skillization
|
||||
- the generic first-party `Phase S1-B` skill settings/menu/off-state control
|
||||
pass is now consumed
|
||||
- the bounded first-party `Phase S1-B` skill settings/menu/off-state control
|
||||
packet is now landed in current code
|
||||
- `S1-C` invocation/provenance/audit ledger is now the next clean skillization
|
||||
move
|
||||
- the generic source-backed `Phase 6R-R` shared classic-cube recognition multi-face
|
||||
correction/explanation control pass is now consumed
|
||||
|
|
|
|||
|
|
@ -54,13 +54,15 @@ Includes:
|
|||
|
||||
- landed now:
|
||||
- skill registry/manifest contract
|
||||
- enable/disable controls
|
||||
- visibility grouping
|
||||
- command/service binding declarations
|
||||
- permission/scope declarations
|
||||
- provenance/log declarations
|
||||
- validation harness
|
||||
- still deferred:
|
||||
- enable/disable controls
|
||||
- provenance/invocation ledger execution
|
||||
- authoring template expansion
|
||||
|
||||
### Analyzer and review skills
|
||||
|
||||
|
|
@ -126,9 +128,10 @@ A coding model working on HyperTwist should still be able to:
|
|||
|
||||
The next correct skillization sequence is:
|
||||
|
||||
1. settings/menu/off-state controls
|
||||
2. permissive analyzer wrappers
|
||||
3. clean-room command-contract specs for restrictive lanes
|
||||
1. invocation/provenance/audit ledger
|
||||
2. authoring/examples/validation harness expansion
|
||||
3. permissive analyzer wrappers
|
||||
4. clean-room command-contract specs for restrictive lanes
|
||||
|
||||
Broader memory, provider, and domain skill widening should wait until that
|
||||
base exists.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue