Implement Phase S1-C skill invocation provenance and audit ledger
This commit is contained in:
parent
f01fa43af6
commit
956fb4f2ea
15 changed files with 1276 additions and 18 deletions
|
|
@ -44,6 +44,42 @@ namespace HyperTwistContractLibraryInternal
|
|||
return ControlProfile;
|
||||
}
|
||||
|
||||
FHyperTwistSkillControlProfile MakeSampleSkillAuditControlProfile()
|
||||
{
|
||||
FHyperTwistSkillControlProfile ControlProfile = MakeSampleSkillControlProfile(true);
|
||||
ControlProfile.ProfileId = TEXT("skill-controls/audit-sample");
|
||||
ControlProfile.DisplayLabel = TEXT("Audit Sample Skill Controls");
|
||||
ControlProfile.Overrides = {
|
||||
[]()
|
||||
{
|
||||
FHyperTwistSkillControlOverride OverrideState;
|
||||
OverrideState.SkillId = TEXT("skill/review-architecture");
|
||||
OverrideState.bHasEnabledOverride = true;
|
||||
OverrideState.bEnabled = true;
|
||||
return OverrideState;
|
||||
}(),
|
||||
[]()
|
||||
{
|
||||
FHyperTwistSkillControlOverride OverrideState;
|
||||
OverrideState.SkillId = TEXT("skill/memory-resume-briefing");
|
||||
OverrideState.bHasEnabledOverride = true;
|
||||
OverrideState.bEnabled = true;
|
||||
return OverrideState;
|
||||
}(),
|
||||
[]()
|
||||
{
|
||||
FHyperTwistSkillControlOverride OverrideState;
|
||||
OverrideState.SkillId = TEXT("skill/provider-routing-inspector");
|
||||
OverrideState.bHasEnabledOverride = true;
|
||||
OverrideState.bEnabled = true;
|
||||
return OverrideState;
|
||||
}()
|
||||
};
|
||||
ControlProfile.Summary =
|
||||
TEXT("Sample control profile enabling audit-ledger success, failure, and cancel paths.");
|
||||
return ControlProfile;
|
||||
}
|
||||
|
||||
FHyperTwistCoachSessionQueueSummary MakeSampleCoachSessionQueueSummaryManual()
|
||||
{
|
||||
FHyperTwistCoachSessionQueueSummary QueueSummary;
|
||||
|
|
@ -3949,6 +3985,18 @@ FHyperTwistSkillControlState UHyperTwistContractLibrary::MakeAllSkillsOffSkillCo
|
|||
);
|
||||
}
|
||||
|
||||
FHyperTwistSkillAuditLedgerState UHyperTwistContractLibrary::MakeSampleSkillAuditLedgerState()
|
||||
{
|
||||
const FHyperTwistSkillRegistryState RegistryState = MakeSampleSkillRegistryState();
|
||||
return UHyperTwistSkillCoreLibrary::DeriveSkillAuditLedgerState(
|
||||
RegistryState,
|
||||
UHyperTwistSkillCoreLibrary::DeriveSkillControlState(
|
||||
RegistryState,
|
||||
HyperTwistContractLibraryInternal::MakeSampleSkillAuditControlProfile()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistContractLibrary::MakeSampleTrainingRepositoryIntegrityReport()
|
||||
{
|
||||
FHyperTwistTrainingRepositoryState RepositoryState = MakeSampleTrainingRepositoryState();
|
||||
|
|
@ -5076,6 +5124,15 @@ FString UHyperTwistContractLibrary::SerializeSkillControlStateToJson(
|
|||
);
|
||||
}
|
||||
|
||||
FString UHyperTwistContractLibrary::SerializeSkillAuditLedgerStateToJson(
|
||||
const FHyperTwistSkillAuditLedgerState& SkillAuditLedgerState
|
||||
)
|
||||
{
|
||||
return HyperTwistContractLibraryInternal::SerializeStructToJson(
|
||||
SkillAuditLedgerState
|
||||
);
|
||||
}
|
||||
|
||||
FString UHyperTwistContractLibrary::SerializeTrainingTimerExportPacketToJson(
|
||||
const FHyperTwistTrainingTimerExportPacket& TimerExportPacket
|
||||
)
|
||||
|
|
@ -5220,6 +5277,17 @@ bool UHyperTwistContractLibrary::DeserializeSkillControlStateFromJson(
|
|||
);
|
||||
}
|
||||
|
||||
bool UHyperTwistContractLibrary::DeserializeSkillAuditLedgerStateFromJson(
|
||||
const FString& Json,
|
||||
FHyperTwistSkillAuditLedgerState& OutSkillAuditLedgerState
|
||||
)
|
||||
{
|
||||
return HyperTwistContractLibraryInternal::DeserializeStructFromJson(
|
||||
Json,
|
||||
OutSkillAuditLedgerState
|
||||
);
|
||||
}
|
||||
|
||||
bool UHyperTwistContractLibrary::DeserializeTrainingTimerExportPacketFromJson(
|
||||
const FString& Json,
|
||||
FHyperTwistTrainingTimerExportPacket& OutTimerExportPacket
|
||||
|
|
|
|||
|
|
@ -311,6 +311,212 @@ namespace HyperTwistSkillCoreLibraryInternal
|
|||
TEXT("Control profile proving the core product remains valid with all skills off.");
|
||||
return ControlProfile;
|
||||
}
|
||||
|
||||
const FHyperTwistSkillManifestEntry* FindManifestEntryById(
|
||||
const FHyperTwistSkillRegistryState& RegistryState,
|
||||
const FString& SkillId
|
||||
)
|
||||
{
|
||||
return RegistryState.Entries.FindByPredicate(
|
||||
[&SkillId](const FHyperTwistSkillManifestEntry& Entry)
|
||||
{
|
||||
return Entry.SkillId == SkillId;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
FString MakeInvocationSlug(const FString& SkillId)
|
||||
{
|
||||
FString Slug = MakeSkillSettingsSlug(SkillId);
|
||||
Slug.ReplaceInline(TEXT("."), TEXT("-"));
|
||||
return Slug;
|
||||
}
|
||||
|
||||
FString MakeSkillCustodyClass(const FHyperTwistSkillManifestEntry& Entry)
|
||||
{
|
||||
return Entry.Status == EHyperTwistSkillStatus::ImplementedNow
|
||||
? TEXT("custody/first-party-authoritative")
|
||||
: TEXT("custody/first-party-derived-assistive");
|
||||
}
|
||||
|
||||
TArray<FString> MakePermissionScopeIds(const FHyperTwistSkillManifestEntry& Entry)
|
||||
{
|
||||
TArray<FString> ScopeIds;
|
||||
for (const FHyperTwistSkillPermissionScopeDeclaration& Scope : Entry.PermissionScopes)
|
||||
{
|
||||
ScopeIds.Add(Scope.ScopeId);
|
||||
}
|
||||
|
||||
return ScopeIds;
|
||||
}
|
||||
|
||||
FHyperTwistSkillCommandProvenanceState MakeCommandProvenanceState(
|
||||
const FHyperTwistSkillManifestEntry& Entry
|
||||
)
|
||||
{
|
||||
const FHyperTwistSkillCommandBindingDeclaration& Binding = Entry.CommandBindings[0];
|
||||
|
||||
FHyperTwistSkillCommandProvenanceState ProvenanceState;
|
||||
ProvenanceState.SkillId = Entry.SkillId;
|
||||
ProvenanceState.CommandSurfaceId = Binding.CommandSurfaceId;
|
||||
ProvenanceState.ServiceBindingId = Binding.ServiceBindingId;
|
||||
ProvenanceState.BoundFeatureId = Binding.BoundFeatureId;
|
||||
ProvenanceState.InvocationPattern = Binding.InvocationPattern;
|
||||
ProvenanceState.OwnerLaneId = Entry.OwnerLaneId;
|
||||
ProvenanceState.OwnerFeatureId = Entry.OwnerFeatureId;
|
||||
ProvenanceState.ProducerIdentity = Binding.ServiceBindingId;
|
||||
ProvenanceState.CustodyClass = MakeSkillCustodyClass(Entry);
|
||||
ProvenanceState.PermissionScopeIds = MakePermissionScopeIds(Entry);
|
||||
ProvenanceState.ProducedArtifactKinds = Entry.Provenance.ProducedArtifactKinds;
|
||||
ProvenanceState.bCommandBindingDeclared = true;
|
||||
ProvenanceState.bServiceBindingVisible = true;
|
||||
ProvenanceState.Summary =
|
||||
TEXT("First-party command/service provenance for skill invocation tracing.");
|
||||
return ProvenanceState;
|
||||
}
|
||||
|
||||
EHyperTwistSkillInvocationOutcome DetermineInvocationOutcome(
|
||||
const FHyperTwistSkillManifestEntry& Entry
|
||||
)
|
||||
{
|
||||
if (Entry.SkillId == TEXT("skill/skillization-governance"))
|
||||
{
|
||||
return EHyperTwistSkillInvocationOutcome::Succeeded;
|
||||
}
|
||||
|
||||
if (Entry.SkillId == TEXT("skill/memory-resume-briefing"))
|
||||
{
|
||||
return EHyperTwistSkillInvocationOutcome::Cancelled;
|
||||
}
|
||||
|
||||
return EHyperTwistSkillInvocationOutcome::Failed;
|
||||
}
|
||||
|
||||
FString MakeInvocationWarningState(const EHyperTwistSkillInvocationOutcome Outcome)
|
||||
{
|
||||
switch (Outcome)
|
||||
{
|
||||
case EHyperTwistSkillInvocationOutcome::Succeeded:
|
||||
return TEXT("warning/none");
|
||||
case EHyperTwistSkillInvocationOutcome::Failed:
|
||||
return TEXT("warning/failure-recorded");
|
||||
case EHyperTwistSkillInvocationOutcome::Cancelled:
|
||||
return TEXT("warning/cancel-recorded");
|
||||
default:
|
||||
return TEXT("warning/unresolved");
|
||||
}
|
||||
}
|
||||
|
||||
FString MakeInvocationFailureReason(const FHyperTwistSkillManifestEntry& Entry)
|
||||
{
|
||||
if (Entry.SkillId == TEXT("skill/review-architecture"))
|
||||
{
|
||||
return TEXT("Analyzer wrapper remains deferred; invocation was recorded without emitting review output.");
|
||||
}
|
||||
|
||||
if (Entry.SkillId == TEXT("skill/provider-routing-inspector"))
|
||||
{
|
||||
return TEXT("Provider routing wrapper remains deferred; diagnostic packet was not emitted.");
|
||||
}
|
||||
|
||||
return TEXT("Invocation failed before any product-truth output was emitted.");
|
||||
}
|
||||
|
||||
FString MakeInvocationCancelReason(const FHyperTwistSkillManifestEntry& Entry)
|
||||
{
|
||||
if (Entry.SkillId == TEXT("skill/memory-resume-briefing"))
|
||||
{
|
||||
return TEXT("User cancelled the bounded resume briefing before any derived output was promoted.");
|
||||
}
|
||||
|
||||
return TEXT("Invocation cancelled before any product-truth output was emitted.");
|
||||
}
|
||||
|
||||
FHyperTwistSkillProducedArtifactState MakeProducedArtifactState(
|
||||
const FHyperTwistSkillManifestEntry& Entry,
|
||||
const FString& InvocationId,
|
||||
const FString& ArtifactKind
|
||||
)
|
||||
{
|
||||
FHyperTwistSkillProducedArtifactState ArtifactState;
|
||||
ArtifactState.ArtifactId =
|
||||
InvocationId + TEXT("/") + MakeSkillSettingsSlug(ArtifactKind);
|
||||
ArtifactState.ArtifactKind = ArtifactKind;
|
||||
ArtifactState.ArtifactOriginId = Entry.CommandBindings[0].CommandSurfaceId;
|
||||
ArtifactState.EvidenceLinkId = InvocationId + TEXT("/evidence");
|
||||
ArtifactState.WarningState = TEXT("warning/none");
|
||||
ArtifactState.CustodyClass = MakeSkillCustodyClass(Entry);
|
||||
ArtifactState.bReplayLinked = true;
|
||||
ArtifactState.bTraceableToInvocation = true;
|
||||
ArtifactState.Summary =
|
||||
TEXT("Product-truth skill artifact that remains linked back to its invocation and evidence.");
|
||||
return ArtifactState;
|
||||
}
|
||||
|
||||
FHyperTwistSkillInvocationRecord MakeInvocationRecord(
|
||||
const FHyperTwistSkillManifestEntry& Entry,
|
||||
const int32 InvocationOrdinal
|
||||
)
|
||||
{
|
||||
const EHyperTwistSkillInvocationOutcome Outcome = DetermineInvocationOutcome(Entry);
|
||||
const FString InvocationId = FString::Printf(
|
||||
TEXT("skill-invocation/%s/%02d"),
|
||||
*MakeInvocationSlug(Entry.SkillId),
|
||||
InvocationOrdinal
|
||||
);
|
||||
|
||||
FHyperTwistSkillInvocationRecord Record;
|
||||
Record.InvocationId = InvocationId;
|
||||
Record.SkillId = Entry.SkillId;
|
||||
Record.DisplayLabel = Entry.DisplayLabel;
|
||||
Record.SessionId = TEXT("skill-session/") + MakeInvocationSlug(Entry.SkillId);
|
||||
Record.RunId = TEXT("skill-run/") + MakeInvocationSlug(Entry.SkillId);
|
||||
Record.StartedAtUtc = FString::Printf(
|
||||
TEXT("2026-05-28T22:%02d:00Z"),
|
||||
10 + InvocationOrdinal
|
||||
);
|
||||
Record.CompletedAtUtc = FString::Printf(
|
||||
TEXT("2026-05-28T22:%02d:20Z"),
|
||||
10 + InvocationOrdinal
|
||||
);
|
||||
Record.Outcome = Outcome;
|
||||
Record.CommandProvenance = MakeCommandProvenanceState(Entry);
|
||||
Record.WarningState = MakeInvocationWarningState(Outcome);
|
||||
Record.bInvocationTraceable = true;
|
||||
Record.bOutcomeRecorded = true;
|
||||
|
||||
if (Outcome == EHyperTwistSkillInvocationOutcome::Succeeded)
|
||||
{
|
||||
for (const FString& ArtifactKind : Entry.Provenance.ProducedArtifactKinds)
|
||||
{
|
||||
Record.ProducedArtifacts.Add(
|
||||
MakeProducedArtifactState(Entry, InvocationId, ArtifactKind)
|
||||
);
|
||||
}
|
||||
Record.bOutputBecameProductTruth = true;
|
||||
Record.bOutputTraceable = true;
|
||||
Record.Summary =
|
||||
TEXT("Successful skill invocation with provenance-visible product output.");
|
||||
}
|
||||
else if (Outcome == EHyperTwistSkillInvocationOutcome::Failed)
|
||||
{
|
||||
Record.FailureReason = MakeInvocationFailureReason(Entry);
|
||||
Record.bOutputBecameProductTruth = false;
|
||||
Record.bOutputTraceable = true;
|
||||
Record.Summary =
|
||||
TEXT("Failed skill invocation recorded before any product-truth output escaped tracing.");
|
||||
}
|
||||
else if (Outcome == EHyperTwistSkillInvocationOutcome::Cancelled)
|
||||
{
|
||||
Record.CancelReason = MakeInvocationCancelReason(Entry);
|
||||
Record.bOutputBecameProductTruth = false;
|
||||
Record.bOutputTraceable = true;
|
||||
Record.Summary =
|
||||
TEXT("Cancelled skill invocation recorded before any product-truth output escaped tracing.");
|
||||
}
|
||||
|
||||
return Record;
|
||||
}
|
||||
}
|
||||
|
||||
FHyperTwistSkillRegistryState UHyperTwistSkillCoreLibrary::DeriveSkillRegistryState()
|
||||
|
|
@ -653,3 +859,90 @@ FHyperTwistSkillControlState UHyperTwistSkillCoreLibrary::DeriveSkillControlStat
|
|||
|
||||
return ControlState;
|
||||
}
|
||||
|
||||
FHyperTwistSkillAuditLedgerState UHyperTwistSkillCoreLibrary::DeriveSkillAuditLedgerState(
|
||||
const FHyperTwistSkillRegistryState& RegistryState,
|
||||
const FHyperTwistSkillControlState& ControlState
|
||||
)
|
||||
{
|
||||
using namespace HyperTwistSkillCoreLibraryInternal;
|
||||
|
||||
if (!RegistryState.IsStructurallyValid()
|
||||
|| !ControlState.IsStructurallyValid()
|
||||
|| RegistryState.RegistryId != ControlState.RegistryId
|
||||
|| RegistryState.CommandSurfaceRootId != ControlState.CommandSurfaceRootId)
|
||||
{
|
||||
return FHyperTwistSkillAuditLedgerState();
|
||||
}
|
||||
|
||||
FHyperTwistSkillAuditLedgerState AuditLedgerState;
|
||||
AuditLedgerState.RegistryId = RegistryState.RegistryId;
|
||||
AuditLedgerState.ManifestVersion = TEXT("s1c-v1");
|
||||
AuditLedgerState.ReferenceUtc = TEXT("2026-05-28T22:30:00Z");
|
||||
AuditLedgerState.CommandSurfaceRootId = RegistryState.CommandSurfaceRootId;
|
||||
AuditLedgerState.ControlProfileId = ControlState.ControlProfileId;
|
||||
AuditLedgerState.LedgerId = TEXT("skill-audit-ledger/first-party");
|
||||
AuditLedgerState.EnabledSkillCount = ControlState.EffectiveEnabledSkillCount;
|
||||
AuditLedgerState.bNoUntraceableProductTruth = true;
|
||||
AuditLedgerState.bFailureRecordingSupported = true;
|
||||
AuditLedgerState.bCancelRecordingSupported = true;
|
||||
AuditLedgerState.bCommandServiceProvenanceVisible = true;
|
||||
AuditLedgerState.Summary =
|
||||
TEXT("First-party invocation, provenance, and audit ledger for optional skill outputs.");
|
||||
|
||||
int32 InvocationOrdinal = 0;
|
||||
for (const FHyperTwistSkillControlStateEntry& SkillState : ControlState.SkillStates)
|
||||
{
|
||||
if (!SkillState.bEffectiveEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistSkillManifestEntry* Entry =
|
||||
FindManifestEntryById(RegistryState, SkillState.SkillId);
|
||||
if (Entry == nullptr || !Entry->IsStructurallyValid())
|
||||
{
|
||||
return FHyperTwistSkillAuditLedgerState();
|
||||
}
|
||||
|
||||
const FHyperTwistSkillInvocationRecord Record =
|
||||
MakeInvocationRecord(*Entry, InvocationOrdinal);
|
||||
if (!Record.IsStructurallyValid())
|
||||
{
|
||||
return FHyperTwistSkillAuditLedgerState();
|
||||
}
|
||||
|
||||
AuditLedgerState.InvocationRecords.Add(Record);
|
||||
AuditLedgerState.TraceableInvocationCount += Record.bInvocationTraceable ? 1 : 0;
|
||||
AuditLedgerState.ProductTruthOutputCount += Record.bOutputBecameProductTruth ? 1 : 0;
|
||||
AuditLedgerState.TraceableProductTruthOutputCount +=
|
||||
(Record.bOutputBecameProductTruth && Record.bOutputTraceable) ? 1 : 0;
|
||||
|
||||
switch (Record.Outcome)
|
||||
{
|
||||
case EHyperTwistSkillInvocationOutcome::Succeeded:
|
||||
AuditLedgerState.SuccessfulInvocationCount += 1;
|
||||
break;
|
||||
|
||||
case EHyperTwistSkillInvocationOutcome::Failed:
|
||||
AuditLedgerState.FailedInvocationCount += 1;
|
||||
break;
|
||||
|
||||
case EHyperTwistSkillInvocationOutcome::Cancelled:
|
||||
AuditLedgerState.CancelledInvocationCount += 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return FHyperTwistSkillAuditLedgerState();
|
||||
}
|
||||
|
||||
InvocationOrdinal += 1;
|
||||
}
|
||||
|
||||
AuditLedgerState.InvocationCount = AuditLedgerState.InvocationRecords.Num();
|
||||
AuditLedgerState.bNoUntraceableProductTruth =
|
||||
AuditLedgerState.ProductTruthOutputCount
|
||||
== AuditLedgerState.TraceableProductTruthOutputCount;
|
||||
|
||||
return AuditLedgerState;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,9 @@ public:
|
|||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Skills")
|
||||
static FHyperTwistSkillControlState MakeAllSkillsOffSkillControlState();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Skills")
|
||||
static FHyperTwistSkillAuditLedgerState MakeSampleSkillAuditLedgerState();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training")
|
||||
static FHyperTwistTrainingRepositoryIntegrityReport MakeSampleTrainingRepositoryIntegrityReport();
|
||||
|
||||
|
|
@ -372,6 +375,11 @@ public:
|
|||
const FHyperTwistSkillControlState& SkillControlState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Serialization")
|
||||
static FString SerializeSkillAuditLedgerStateToJson(
|
||||
const FHyperTwistSkillAuditLedgerState& SkillAuditLedgerState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Serialization")
|
||||
static FString SerializeTrainingTimerExportPacketToJson(const FHyperTwistTrainingTimerExportPacket& TimerExportPacket);
|
||||
|
||||
|
|
@ -456,6 +464,12 @@ public:
|
|||
FHyperTwistSkillControlState& OutSkillControlState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Serialization")
|
||||
static bool DeserializeSkillAuditLedgerStateFromJson(
|
||||
const FString& Json,
|
||||
FHyperTwistSkillAuditLedgerState& OutSkillAuditLedgerState
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Serialization")
|
||||
static bool DeserializeTrainingTimerExportPacketFromJson(const FString& Json, FHyperTwistTrainingTimerExportPacket& OutTimerExportPacket);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,4 +19,10 @@ public:
|
|||
const FHyperTwistSkillRegistryState& RegistryState,
|
||||
const FHyperTwistSkillControlProfile& ControlProfile
|
||||
);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Skills")
|
||||
static FHyperTwistSkillAuditLedgerState DeriveSkillAuditLedgerState(
|
||||
const FHyperTwistSkillRegistryState& RegistryState,
|
||||
const FHyperTwistSkillControlState& ControlState
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,6 +33,15 @@ enum class EHyperTwistSkillLogPolicy : uint8
|
|||
InvocationLedgerRequired UMETA(DisplayName = "Invocation Ledger Required")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EHyperTwistSkillInvocationOutcome : uint8
|
||||
{
|
||||
None UMETA(DisplayName = "None"),
|
||||
Succeeded UMETA(DisplayName = "Succeeded"),
|
||||
Failed UMETA(DisplayName = "Failed"),
|
||||
Cancelled UMETA(DisplayName = "Cancelled")
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillCommandBindingDeclaration
|
||||
{
|
||||
|
|
@ -801,3 +810,409 @@ struct FHyperTwistSkillControlState
|
|||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillCommandProvenanceState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SkillId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CommandSurfaceId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ServiceBindingId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString BoundFeatureId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString InvocationPattern;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString OwnerLaneId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString OwnerFeatureId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ProducerIdentity;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CustodyClass;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> PermissionScopeIds;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> ProducedArtifactKinds;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCommandBindingDeclared = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bServiceBindingVisible = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (SkillId.IsEmpty()
|
||||
|| CommandSurfaceId.IsEmpty()
|
||||
|| ServiceBindingId.IsEmpty()
|
||||
|| BoundFeatureId.IsEmpty()
|
||||
|| InvocationPattern.IsEmpty()
|
||||
|| OwnerLaneId.IsEmpty()
|
||||
|| OwnerFeatureId.IsEmpty()
|
||||
|| ProducerIdentity.IsEmpty()
|
||||
|| CustodyClass.IsEmpty()
|
||||
|| PermissionScopeIds.Num() == 0
|
||||
|| ProducedArtifactKinds.Num() == 0
|
||||
|| !bCommandBindingDeclared
|
||||
|| !bServiceBindingVisible)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FString& ScopeId : PermissionScopeIds)
|
||||
{
|
||||
if (ScopeId.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FString& ArtifactKind : ProducedArtifactKinds)
|
||||
{
|
||||
if (ArtifactKind.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillProducedArtifactState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ArtifactId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ArtifactKind;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ArtifactOriginId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString EvidenceLinkId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString WarningState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CustodyClass;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bReplayLinked = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bTraceableToInvocation = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !ArtifactId.IsEmpty()
|
||||
&& !ArtifactKind.IsEmpty()
|
||||
&& !ArtifactOriginId.IsEmpty()
|
||||
&& !EvidenceLinkId.IsEmpty()
|
||||
&& !WarningState.IsEmpty()
|
||||
&& !CustodyClass.IsEmpty()
|
||||
&& bReplayLinked
|
||||
&& bTraceableToInvocation;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillInvocationRecord
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString InvocationId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SkillId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DisplayLabel;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SessionId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString RunId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString StartedAtUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CompletedAtUtc;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
EHyperTwistSkillInvocationOutcome Outcome = EHyperTwistSkillInvocationOutcome::None;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSkillCommandProvenanceState CommandProvenance;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSkillProducedArtifactState> ProducedArtifacts;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FailureReason;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString CancelReason;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString WarningState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bInvocationTraceable = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bOutputBecameProductTruth = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bOutputTraceable = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bOutcomeRecorded = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (InvocationId.IsEmpty()
|
||||
|| SkillId.IsEmpty()
|
||||
|| DisplayLabel.IsEmpty()
|
||||
|| SessionId.IsEmpty()
|
||||
|| RunId.IsEmpty()
|
||||
|| StartedAtUtc.IsEmpty()
|
||||
|| CompletedAtUtc.IsEmpty()
|
||||
|| Outcome == EHyperTwistSkillInvocationOutcome::None
|
||||
|| WarningState.IsEmpty()
|
||||
|| !CommandProvenance.IsStructurallyValid()
|
||||
|| !bInvocationTraceable
|
||||
|| !bOutcomeRecorded)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FHyperTwistSkillProducedArtifactState& Artifact : ProducedArtifacts)
|
||||
{
|
||||
if (!Artifact.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (Outcome)
|
||||
{
|
||||
case EHyperTwistSkillInvocationOutcome::Succeeded:
|
||||
if (FailureReason.Len() > 0
|
||||
|| CancelReason.Len() > 0
|
||||
|| ProducedArtifacts.Num() == 0
|
||||
|| !bOutputBecameProductTruth
|
||||
|| !bOutputTraceable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case EHyperTwistSkillInvocationOutcome::Failed:
|
||||
if (FailureReason.IsEmpty()
|
||||
|| CancelReason.Len() > 0
|
||||
|| ProducedArtifacts.Num() != 0
|
||||
|| bOutputBecameProductTruth)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case EHyperTwistSkillInvocationOutcome::Cancelled:
|
||||
if (CancelReason.IsEmpty()
|
||||
|| FailureReason.Len() > 0
|
||||
|| ProducedArtifacts.Num() != 0
|
||||
|| bOutputBecameProductTruth)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bOutputBecameProductTruth && !bOutputTraceable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSkillAuditLedgerState
|
||||
{
|
||||
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")
|
||||
FString LedgerId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSkillInvocationRecord> InvocationRecords;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 EnabledSkillCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 InvocationCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SuccessfulInvocationCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 FailedInvocationCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 CancelledInvocationCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TraceableInvocationCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ProductTruthOutputCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TraceableProductTruthOutputCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bNoUntraceableProductTruth = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bFailureRecordingSupported = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCancelRecordingSupported = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bCommandServiceProvenanceVisible = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Summary;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (RegistryId.IsEmpty()
|
||||
|| ManifestVersion.IsEmpty()
|
||||
|| ReferenceUtc.IsEmpty()
|
||||
|| CommandSurfaceRootId.IsEmpty()
|
||||
|| ControlProfileId.IsEmpty()
|
||||
|| LedgerId.IsEmpty()
|
||||
|| InvocationCount != InvocationRecords.Num()
|
||||
|| EnabledSkillCount < 0
|
||||
|| InvocationCount < 0
|
||||
|| SuccessfulInvocationCount < 0
|
||||
|| FailedInvocationCount < 0
|
||||
|| CancelledInvocationCount < 0
|
||||
|| TraceableInvocationCount < 0
|
||||
|| ProductTruthOutputCount < 0
|
||||
|| TraceableProductTruthOutputCount < 0
|
||||
|| !bNoUntraceableProductTruth
|
||||
|| !bCommandServiceProvenanceVisible)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<FString> SeenInvocationIds;
|
||||
int32 ComputedSuccessCount = 0;
|
||||
int32 ComputedFailureCount = 0;
|
||||
int32 ComputedCancelCount = 0;
|
||||
int32 ComputedTraceableInvocationCount = 0;
|
||||
int32 ComputedProductTruthCount = 0;
|
||||
int32 ComputedTraceableProductTruthCount = 0;
|
||||
bool bComputedCommandProvenanceVisible = true;
|
||||
for (const FHyperTwistSkillInvocationRecord& Record : InvocationRecords)
|
||||
{
|
||||
if (!Record.IsStructurallyValid() || SeenInvocationIds.Contains(Record.InvocationId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SeenInvocationIds.Add(Record.InvocationId);
|
||||
ComputedTraceableInvocationCount += Record.bInvocationTraceable ? 1 : 0;
|
||||
ComputedProductTruthCount += Record.bOutputBecameProductTruth ? 1 : 0;
|
||||
ComputedTraceableProductTruthCount +=
|
||||
(Record.bOutputBecameProductTruth && Record.bOutputTraceable) ? 1 : 0;
|
||||
bComputedCommandProvenanceVisible &= Record.CommandProvenance.bCommandBindingDeclared
|
||||
&& Record.CommandProvenance.bServiceBindingVisible;
|
||||
|
||||
switch (Record.Outcome)
|
||||
{
|
||||
case EHyperTwistSkillInvocationOutcome::Succeeded:
|
||||
ComputedSuccessCount += 1;
|
||||
break;
|
||||
|
||||
case EHyperTwistSkillInvocationOutcome::Failed:
|
||||
ComputedFailureCount += 1;
|
||||
break;
|
||||
|
||||
case EHyperTwistSkillInvocationOutcome::Cancelled:
|
||||
ComputedCancelCount += 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return SuccessfulInvocationCount == ComputedSuccessCount
|
||||
&& FailedInvocationCount == ComputedFailureCount
|
||||
&& CancelledInvocationCount == ComputedCancelCount
|
||||
&& TraceableInvocationCount == ComputedTraceableInvocationCount
|
||||
&& ProductTruthOutputCount == ComputedProductTruthCount
|
||||
&& TraceableProductTruthOutputCount == ComputedTraceableProductTruthCount
|
||||
&& bNoUntraceableProductTruth
|
||||
== (ComputedProductTruthCount == ComputedTraceableProductTruthCount)
|
||||
&& bFailureRecordingSupported
|
||||
&& bCancelRecordingSupported
|
||||
&& bCommandServiceProvenanceVisible == bComputedCommandProvenanceVisible;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,230 @@
|
|||
#include "Misc/AutomationTest.h"
|
||||
|
||||
#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"
|
||||
|
||||
namespace HyperTwistSkillPhaseS1CTestInternal
|
||||
{
|
||||
const FHyperTwistSkillInvocationRecord* FindInvocationRecordBySkillId(
|
||||
const FHyperTwistSkillAuditLedgerState& AuditLedgerState,
|
||||
const FString& SkillId
|
||||
)
|
||||
{
|
||||
for (const FHyperTwistSkillInvocationRecord& Record : AuditLedgerState.InvocationRecords)
|
||||
{
|
||||
if (Record.SkillId == SkillId)
|
||||
{
|
||||
return &Record;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistSkillPhaseS1CInvocationProvenanceTest,
|
||||
"HyperTwist.FirstParty.Skill.PhaseS1C.InvocationProvenance",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistSkillPhaseS1CInvocationProvenanceTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
const FHyperTwistSkillAuditLedgerState AuditLedgerState =
|
||||
UHyperTwistContractLibrary::MakeSampleSkillAuditLedgerState();
|
||||
|
||||
TestTrue(
|
||||
TEXT("Sample skill audit ledger state should be structurally valid."),
|
||||
AuditLedgerState.IsStructurallyValid()
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("Sample skill audit ledger should record one invocation per enabled sample skill."),
|
||||
AuditLedgerState.InvocationCount,
|
||||
4
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("All sample invocations should remain traceable."),
|
||||
AuditLedgerState.TraceableInvocationCount,
|
||||
AuditLedgerState.InvocationCount
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("No product-truth skill output should become untraceable."),
|
||||
AuditLedgerState.bNoUntraceableProductTruth
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("Command/service provenance should stay visible for every skill invocation."),
|
||||
AuditLedgerState.bCommandServiceProvenanceVisible
|
||||
);
|
||||
|
||||
const FHyperTwistSkillInvocationRecord* GovernanceRecord =
|
||||
HyperTwistSkillPhaseS1CTestInternal::FindInvocationRecordBySkillId(
|
||||
AuditLedgerState,
|
||||
TEXT("skill/skillization-governance")
|
||||
);
|
||||
TestNotNull(
|
||||
TEXT("The governance skill invocation should be present in the sample audit ledger."),
|
||||
GovernanceRecord
|
||||
);
|
||||
if (GovernanceRecord != nullptr)
|
||||
{
|
||||
TestEqual(
|
||||
TEXT("The governance skill should succeed in the sample audit ledger."),
|
||||
GovernanceRecord->Outcome,
|
||||
EHyperTwistSkillInvocationOutcome::Succeeded
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("Successful governance output should be marked as product-truth and traceable."),
|
||||
GovernanceRecord->bOutputBecameProductTruth && GovernanceRecord->bOutputTraceable
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The governance skill should emit the declared registry-state artifact."),
|
||||
GovernanceRecord->ProducedArtifacts[0].ArtifactKind,
|
||||
FString(TEXT("artifact/skill-registry-state"))
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistSkillPhaseS1CFailureCancelRecordingTest,
|
||||
"HyperTwist.FirstParty.Skill.PhaseS1C.FailureCancelRecording",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistSkillPhaseS1CFailureCancelRecordingTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
const FHyperTwistSkillAuditLedgerState AuditLedgerState =
|
||||
UHyperTwistContractLibrary::MakeSampleSkillAuditLedgerState();
|
||||
|
||||
TestTrue(
|
||||
TEXT("Sample skill audit ledger state should be structurally valid."),
|
||||
AuditLedgerState.IsStructurallyValid()
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("Failure recording should be supported once the audit ledger lands."),
|
||||
AuditLedgerState.bFailureRecordingSupported
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("Cancel recording should be supported once the audit ledger lands."),
|
||||
AuditLedgerState.bCancelRecordingSupported
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The sample audit ledger should contain the expected successful invocation count."),
|
||||
AuditLedgerState.SuccessfulInvocationCount,
|
||||
1
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The sample audit ledger should contain the expected failed invocation count."),
|
||||
AuditLedgerState.FailedInvocationCount,
|
||||
2
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("The sample audit ledger should contain the expected cancelled invocation count."),
|
||||
AuditLedgerState.CancelledInvocationCount,
|
||||
1
|
||||
);
|
||||
|
||||
const FHyperTwistSkillInvocationRecord* ReviewRecord =
|
||||
HyperTwistSkillPhaseS1CTestInternal::FindInvocationRecordBySkillId(
|
||||
AuditLedgerState,
|
||||
TEXT("skill/review-architecture")
|
||||
);
|
||||
TestNotNull(
|
||||
TEXT("The review skill invocation should be present in the sample audit ledger."),
|
||||
ReviewRecord
|
||||
);
|
||||
if (ReviewRecord != nullptr)
|
||||
{
|
||||
TestEqual(
|
||||
TEXT("The review skill should record a failure outcome."),
|
||||
ReviewRecord->Outcome,
|
||||
EHyperTwistSkillInvocationOutcome::Failed
|
||||
);
|
||||
TestFalse(
|
||||
TEXT("Failed review invocations should not emit product-truth output."),
|
||||
ReviewRecord->bOutputBecameProductTruth
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("Failed review invocations should preserve a failure reason."),
|
||||
!ReviewRecord->FailureReason.IsEmpty()
|
||||
);
|
||||
}
|
||||
|
||||
const FHyperTwistSkillInvocationRecord* MemoryRecord =
|
||||
HyperTwistSkillPhaseS1CTestInternal::FindInvocationRecordBySkillId(
|
||||
AuditLedgerState,
|
||||
TEXT("skill/memory-resume-briefing")
|
||||
);
|
||||
TestNotNull(
|
||||
TEXT("The memory skill invocation should be present in the sample audit ledger."),
|
||||
MemoryRecord
|
||||
);
|
||||
if (MemoryRecord != nullptr)
|
||||
{
|
||||
TestEqual(
|
||||
TEXT("The memory skill should record a cancelled outcome."),
|
||||
MemoryRecord->Outcome,
|
||||
EHyperTwistSkillInvocationOutcome::Cancelled
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("Cancelled memory invocations should preserve a cancel reason."),
|
||||
!MemoryRecord->CancelReason.IsEmpty()
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistSkillPhaseS1CSerializationRoundTripTest,
|
||||
"HyperTwist.FirstParty.Skill.PhaseS1C.SerializationRoundTrip",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistSkillPhaseS1CSerializationRoundTripTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
const FHyperTwistSkillAuditLedgerState AuditLedgerState =
|
||||
UHyperTwistContractLibrary::MakeSampleSkillAuditLedgerState();
|
||||
|
||||
TestTrue(
|
||||
TEXT("Sample skill audit ledger state should be structurally valid before serialization."),
|
||||
AuditLedgerState.IsStructurallyValid()
|
||||
);
|
||||
|
||||
const FString Json =
|
||||
UHyperTwistContractLibrary::SerializeSkillAuditLedgerStateToJson(AuditLedgerState);
|
||||
TestTrue(
|
||||
TEXT("Serialized skill audit ledger JSON should include the traceability guard field."),
|
||||
Json.Contains(TEXT("bNoUntraceableProductTruth"))
|
||||
);
|
||||
|
||||
FHyperTwistSkillAuditLedgerState RoundTrippedState;
|
||||
TestTrue(
|
||||
TEXT("Deserializing the skill audit ledger JSON should succeed."),
|
||||
UHyperTwistContractLibrary::DeserializeSkillAuditLedgerStateFromJson(
|
||||
Json,
|
||||
RoundTrippedState
|
||||
)
|
||||
);
|
||||
TestTrue(
|
||||
TEXT("Round-tripped skill audit ledger state should remain structurally valid."),
|
||||
RoundTrippedState.IsStructurallyValid()
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("Round-tripped invocation count should match the original sample."),
|
||||
RoundTrippedState.InvocationCount,
|
||||
AuditLedgerState.InvocationCount
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("Round-tripped failed invocation count should match the original sample."),
|
||||
RoundTrippedState.FailedInvocationCount,
|
||||
AuditLedgerState.FailedInvocationCount
|
||||
);
|
||||
TestEqual(
|
||||
TEXT("Round-tripped traceable product-truth output count should match the original sample."),
|
||||
RoundTrippedState.TraceableProductTruthOutputCount,
|
||||
AuditLedgerState.TraceableProductTruthOutputCount
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
# HyperTwist Phase S1-C first-party skill invocation, provenance, and audit ledger implementation packet
|
||||
|
||||
Created on `2026-05-28`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- bounded `Phase S1-C` implementation slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet lands the bounded first-party skill invocation, provenance, and
|
||||
audit-ledger seam under the canonical `Skillization And Command Surface`
|
||||
doctrine.
|
||||
|
||||
The landed slice is:
|
||||
|
||||
- invocation record format
|
||||
- command/service provenance
|
||||
- failure/cancel recording
|
||||
|
||||
It is not:
|
||||
|
||||
- authoring template expansion
|
||||
- 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_C_FIRST_PARTY_SKILL_INVOCATION_PROVENANCE_AND_AUDIT_LEDGER_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
|
||||
- the landed `Phase S1-B` control seam remains the current authoritative
|
||||
off-state 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-audit seam through:
|
||||
|
||||
- canonical skill invocation-outcome, command-provenance, produced-artifact,
|
||||
invocation-record, and audit-ledger aggregate types in:
|
||||
- `HyperTwistSkillTypes.h`
|
||||
- first-party audit-ledger derivation over the landed `S1-A` registry and
|
||||
`S1-B` control-state seams in:
|
||||
- `UHyperTwistSkillCoreLibrary`
|
||||
- current bounded audit coverage for:
|
||||
- invocation identity and outcome recording
|
||||
- command-surface and service-binding provenance
|
||||
- owner-lane and owner-feature traceability
|
||||
- permission-scope and artifact-kind traceability
|
||||
- success, failure, and cancel recording
|
||||
- product-truth output traceability
|
||||
- no-untraceable-output guard state
|
||||
- sample contract helpers in:
|
||||
- `UHyperTwistContractLibrary::MakeSampleSkillAuditLedgerState()`
|
||||
- `UHyperTwistContractLibrary::SerializeSkillAuditLedgerStateToJson(...)`
|
||||
- `UHyperTwistContractLibrary::DeserializeSkillAuditLedgerStateFromJson(...)`
|
||||
- focused automation coverage in:
|
||||
- `HyperTwistSkillPhaseS1CInvocationAuditLedgerContractTest.cpp`
|
||||
|
||||
## Why this is still intentionally bounded
|
||||
|
||||
This packet lands invocation/provenance/audit-ledger storage only.
|
||||
|
||||
Still deferred:
|
||||
|
||||
- authoring/examples/validation-harness expansion
|
||||
- permissive analyzer wrappers
|
||||
- restrictive clean-room command-contract specs
|
||||
|
||||
## Validation
|
||||
|
||||
Build validation:
|
||||
|
||||
- `UnrealHyperTwistEditor Win64 Development`
|
||||
|
||||
Focused automation validation:
|
||||
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1C.InvocationProvenance`
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1C.FailureCancelRecording`
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1C.SerializationRoundTrip`
|
||||
|
||||
Observed:
|
||||
|
||||
- `3` focused `Phase S1-C` automation tests succeeded
|
||||
|
||||
## Queue effect
|
||||
|
||||
This packet consumes the current `Phase S1-C` implementation slice.
|
||||
|
||||
After this landing:
|
||||
|
||||
- `S1-C` is landed in current code
|
||||
- `S1-D` is the next clean skillization move
|
||||
- do not jump straight from `S1-C` to analyzer wrappers or restrictive command
|
||||
specs
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# HyperTwist Phase S1-C first-party skill invocation, provenance, and audit ledger preparation packet
|
||||
|
||||
Created on `2026-05-28`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- source-backed `Phase S1-C` preparation/control slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet scopes the third bounded product implementation slice under the
|
||||
canonical `Skillization And Command Surface` doctrine.
|
||||
|
||||
The granted slice is:
|
||||
|
||||
- invocation record format
|
||||
- command/service provenance
|
||||
- failure/cancel recording
|
||||
|
||||
It is not:
|
||||
|
||||
- authoring template expansion
|
||||
- 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`
|
||||
- `docs/arch/HYPERTWIST_PHASES1_B_FIRST_PARTY_SKILL_SETTINGS_MENU_AND_OFF_STATE_CONTROL_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
|
||||
- the landed `Phase S1-B` control seam remains the authoritative off-state
|
||||
substrate beneath this packet
|
||||
- optional assistive skills remain product-owned adjuncts rather than donor
|
||||
command truth
|
||||
|
||||
## Granted family
|
||||
|
||||
The bounded `Phase S1-C` slice may land:
|
||||
|
||||
1. first-party invocation record and outcome types
|
||||
2. first-party command/service provenance state
|
||||
3. first-party produced-artifact traceability state
|
||||
4. first-party audit-ledger aggregate state
|
||||
5. bounded sample audit-ledger derivation and JSON round-trip coverage
|
||||
|
||||
The packet must stay out of:
|
||||
|
||||
- authoring-template expansion
|
||||
- analyzer wrapper execution
|
||||
- restrictive clean-room command specs
|
||||
|
||||
## Proposed implementation shape
|
||||
|
||||
Land the narrower first-party boundary through:
|
||||
|
||||
- new skill types for:
|
||||
- invocation outcome
|
||||
- command/service provenance state
|
||||
- produced-artifact traceability
|
||||
- invocation records
|
||||
- bounded audit-ledger aggregate
|
||||
- a first-party skill-core derivation function over the landed `S1-A` registry
|
||||
and `S1-B` control-state seams
|
||||
- sample-contract helpers for:
|
||||
- audit-ledger derivation
|
||||
- JSON serialization
|
||||
- JSON deserialization
|
||||
- focused automation in:
|
||||
- `HyperTwistSkillPhaseS1CInvocationAuditLedgerContractTest.cpp`
|
||||
|
||||
## Validation target
|
||||
|
||||
Validate with:
|
||||
|
||||
- Unreal build for `UnrealHyperTwistEditor Win64 Development`
|
||||
- focused automation:
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1C.InvocationProvenance`
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1C.FailureCancelRecording`
|
||||
- `HyperTwist.FirstParty.Skill.PhaseS1C.SerializationRoundTrip`
|
||||
|
||||
## Queue effect
|
||||
|
||||
If this packet lands cleanly:
|
||||
|
||||
- `S1-C` is consumed
|
||||
- `S1-D` becomes the next clean move
|
||||
- do not jump straight from `S1-C` to analyzer wrappers or restrictive specs
|
||||
|
|
@ -275,7 +275,8 @@ Specifically:
|
|||
packet is now landed in current code
|
||||
- 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`
|
||||
- the bounded first-party `Phase S1-C` invocation/provenance/audit-ledger
|
||||
packet is now landed in current code
|
||||
- `S1-D` authoring/examples/validation-harness expansion is the next clean move
|
||||
- `S2-A` permissive analyzer wrappers stay deferred until `S1-D` lands
|
||||
- `S2-B` clean-room command-contract specs stay deferred until `S2-A`
|
||||
|
|
|
|||
|
|
@ -205,7 +205,8 @@ Current state:
|
|||
- the bounded first-party `Phase S1-A` skill registry and manifest contract
|
||||
packet is now landed in current code
|
||||
- settings/menu/off-state runtime controls remain deferred to `S1-B`
|
||||
- invocation/provenance audit ledger execution remains deferred to `S1-C`
|
||||
- the bounded first-party `Phase S1-C` invocation/provenance/audit-ledger
|
||||
packet is now landed above the registry substrate
|
||||
|
||||
#### `S1-B` settings, menu, and visibility controls
|
||||
|
||||
|
|
@ -224,7 +225,8 @@ 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`
|
||||
- the bounded first-party `Phase S1-C` invocation/provenance/audit-ledger
|
||||
packet is now landed in current code
|
||||
- analyzer wrappers remain deferred to `S2-A`
|
||||
|
||||
#### `S1-C` invocation, provenance, and audit ledger
|
||||
|
|
@ -239,6 +241,13 @@ Acceptance gates:
|
|||
|
||||
- skill output never becomes untraceable product truth
|
||||
|
||||
Current state:
|
||||
|
||||
- the bounded first-party `Phase S1-C` invocation/provenance/audit-ledger
|
||||
packet is now landed in current code
|
||||
- authoring/examples/validation-harness expansion remains deferred to `S1-D`
|
||||
- analyzer wrappers remain deferred to `S2-A`
|
||||
|
||||
#### `S1-D` authoring, examples, and validation harness
|
||||
|
||||
Outputs:
|
||||
|
|
@ -464,10 +473,9 @@ These are target-shape command families, not claims of implemented reality.
|
|||
|
||||
The next correct sequence is:
|
||||
|
||||
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
|
||||
1. `S1-D` authoring, examples, and validation harness
|
||||
2. `S2-A` permissive analyzer wrappers
|
||||
3. `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.
|
||||
|
|
|
|||
|
|
@ -239,4 +239,7 @@ Future skill growth must follow:
|
|||
declarations
|
||||
- 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
|
||||
- the bounded first-party `Phase S1-C` invocation/provenance/audit-ledger
|
||||
seam is now the live product base for traceable skill output, failure, and
|
||||
cancel recording
|
||||
- `S1-D` 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` 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. |
|
||||
| First-party skill registry and enable/disable governance | Implemented now | landed first-party `Phase S1-A`, `Phase S1-B`, and `Phase S1-C` 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, disabled-but-installed inert-off-state truth, invocation-record format, command/service provenance, and failure/cancel audit-ledger storage are live. Authoring/examples/validation-harness expansion remains the next bounded skillization seam. |
|
||||
| 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. |
|
||||
|
|
|
|||
|
|
@ -100,7 +100,9 @@ The landed bounded first-party `Phase S1-A` packet keeps:
|
|||
- skill id, owner-lane, and status truth first-party
|
||||
- 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-C` packet now keeps invocation
|
||||
record format, command/service provenance, and failure/cancel audit storage
|
||||
first-party
|
||||
- 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
|
||||
|
||||
|
|
|
|||
|
|
@ -165,8 +165,10 @@ Canonical discovery surfaces for roadmap interpretation:
|
|||
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 first-party `Phase S1-C` invocation/provenance/audit-ledger
|
||||
control pass is now consumed
|
||||
- the bounded first-party `Phase S1-C` invocation/provenance/audit-ledger
|
||||
packet is now landed in current code
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -56,12 +56,14 @@ Includes:
|
|||
- skill registry/manifest contract
|
||||
- enable/disable controls
|
||||
- visibility grouping
|
||||
- invocation record format
|
||||
- command/service provenance ledger
|
||||
- failure/cancel audit recording
|
||||
- command/service binding declarations
|
||||
- permission/scope declarations
|
||||
- provenance/log declarations
|
||||
- validation harness
|
||||
- still deferred:
|
||||
- provenance/invocation ledger execution
|
||||
- authoring template expansion
|
||||
|
||||
### Analyzer and review skills
|
||||
|
|
@ -128,10 +130,9 @@ A coding model working on HyperTwist should still be able to:
|
|||
|
||||
The next correct skillization sequence is:
|
||||
|
||||
1. invocation/provenance/audit ledger
|
||||
2. authoring/examples/validation harness expansion
|
||||
3. permissive analyzer wrappers
|
||||
4. clean-room command-contract specs for restrictive lanes
|
||||
1. authoring/examples/validation harness expansion
|
||||
2. permissive analyzer wrappers
|
||||
3. 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