Freeze SCUM log-source tailing run contract
This commit is contained in:
@@ -260,6 +260,7 @@ type RunJobExecutionResultBody struct {
|
||||
SQLiteTemplate *SCUMSQLiteTemplateResultDTO `json:"sqliteTemplate,omitempty"`
|
||||
RCONTemplate *SCUMTypedRCONTemplateResultDTO `json:"rconTemplate,omitempty"`
|
||||
GuardedMutation *SCUMGuardedMutationResultDTO `json:"guardedMutation,omitempty"`
|
||||
ParsedLogBatch *SCUMParsedLogBatchResultDTO `json:"parsedLogBatch,omitempty"`
|
||||
ServerDeploymentEvidence *ServerDeploymentEvidenceBody `json:"serverDeploymentEvidence,omitempty"`
|
||||
DeploymentReceipt *ServerDeploymentExecutionReceiptBody `json:"deploymentReceipt,omitempty"`
|
||||
}
|
||||
@@ -519,7 +520,7 @@ func (request RunJobResultRequest) ToDomain() domain.RunJobResult {
|
||||
Message: request.Message,
|
||||
ErrorCode: request.ErrorCode,
|
||||
Retryable: request.Retryable,
|
||||
ExecutionResult: domain.JobExecutionResult{Kind: request.ExecutionResult.Kind, ProcessState: request.ExecutionResult.ProcessState, ExitClassification: request.ExecutionResult.ExitClassification, ExitCode: request.ExecutionResult.ExitCode, Version: request.ExecutionResult.Version, Checksum: request.ExecutionResult.Checksum, SizeBytes: request.ExecutionResult.SizeBytes, AuditSummary: request.ExecutionResult.AuditSummary, Content: request.ExecutionResult.Content, SQLiteSchemaProbe: SCUMSchemaProbeResultPtrToDomain(request.ExecutionResult.SQLiteSchemaProbe), SQLiteTemplate: SCUMSQLiteTemplateResultPtrToDomain(request.ExecutionResult.SQLiteTemplate), RCONTemplate: SCUMTypedRCONTemplateResultPtrToDomain(request.ExecutionResult.RCONTemplate), GuardedMutation: SCUMGuardedMutationResultPtrToDomain(request.ExecutionResult.GuardedMutation), ServerDeploymentEvidence: serverDeploymentEvidenceToDomain(request.ExecutionResult.ServerDeploymentEvidence), DeploymentReceipt: deploymentReceiptToDomain(request.ExecutionResult.DeploymentReceipt)},
|
||||
ExecutionResult: domain.JobExecutionResult{Kind: request.ExecutionResult.Kind, ProcessState: request.ExecutionResult.ProcessState, ExitClassification: request.ExecutionResult.ExitClassification, ExitCode: request.ExecutionResult.ExitCode, Version: request.ExecutionResult.Version, Checksum: request.ExecutionResult.Checksum, SizeBytes: request.ExecutionResult.SizeBytes, AuditSummary: request.ExecutionResult.AuditSummary, Content: request.ExecutionResult.Content, SQLiteSchemaProbe: SCUMSchemaProbeResultPtrToDomain(request.ExecutionResult.SQLiteSchemaProbe), SQLiteTemplate: SCUMSQLiteTemplateResultPtrToDomain(request.ExecutionResult.SQLiteTemplate), RCONTemplate: SCUMTypedRCONTemplateResultPtrToDomain(request.ExecutionResult.RCONTemplate), GuardedMutation: SCUMGuardedMutationResultPtrToDomain(request.ExecutionResult.GuardedMutation), ParsedLogBatch: SCUMParsedLogBatchResultPtrToDomain(request.ExecutionResult.ParsedLogBatch), ServerDeploymentEvidence: serverDeploymentEvidenceToDomain(request.ExecutionResult.ServerDeploymentEvidence), DeploymentReceipt: deploymentReceiptToDomain(request.ExecutionResult.DeploymentReceipt)},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,6 +211,53 @@ func TestRunJobResultRequestParsesGuardedMutationEnvelope(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunJobResultRequestParsesParsedLogBatchEnvelope(t *testing.T) {
|
||||
payload := `{
|
||||
"runEndpointId":"run-local",
|
||||
"sessionToken":"run-session",
|
||||
"jobId":"job-log",
|
||||
"leaseToken":"lease-log",
|
||||
"attempt":1,
|
||||
"state":"succeeded",
|
||||
"progress":{"percent":100,"message":"done"},
|
||||
"executionResult":{
|
||||
"kind":"log.parsed-events",
|
||||
"parsedLogBatch":{
|
||||
"requestId":"request-log",
|
||||
"jobId":"job-log",
|
||||
"binding":{"serverInstanceId":"server-scum","runBindingId":"runtime-binding-server-scum","runEndpointId":"run-local","pluginId":"server.scum","pluginVersion":"1.0.0","adapterVersion":"scum-live-data-v0","gameVersion":"1.0.0","databaseIdentity":"scum-database"},
|
||||
"status":"succeeded",
|
||||
"sourceKey":"scum-login-events",
|
||||
"streamKey":"scum.login",
|
||||
"parserKey":"scum-login-log-login-parser",
|
||||
"parserVersion":"scum-login-log-v1",
|
||||
"adapterVersion":"scum-live-data-v0",
|
||||
"assetDigest":"sha256:` + strings.Repeat("d", 64) + `",
|
||||
"parserDigest":"sha256:` + strings.Repeat("e", 64) + `",
|
||||
"observedAt":"2026-08-13T00:00:00Z",
|
||||
"resultDigest":"sha256:` + strings.Repeat("b", 64) + `",
|
||||
"firstCursor":{"sourceIdentityDigest":"sha256:` + strings.Repeat("1", 64) + `","streamGeneration":"sha256:` + strings.Repeat("2", 64) + `","sequence":7},
|
||||
"lastCursor":{"sourceIdentityDigest":"sha256:` + strings.Repeat("1", 64) + `","streamGeneration":"sha256:` + strings.Repeat("2", 64) + `","sequence":7},
|
||||
"tailState":"rotated",
|
||||
"replay":true,
|
||||
"eventCount":1,
|
||||
"events":[{"eventType":"scum.login","occurredAt":"2026-08-13T00:00:00Z","cursor":{"sourceIdentityDigest":"sha256:` + strings.Repeat("1", 64) + `","streamGeneration":"sha256:` + strings.Repeat("2", 64) + `","sequence":7},"logicalEventDigest":"sha256:` + strings.Repeat("3", 64) + `","eventDigest":"sha256:` + strings.Repeat("4", 64) + `","payloadDigest":"sha256:` + strings.Repeat("5", 64) + `","payload":{"externalPlayerId":"player-redacted","displayName":"Known Player"}}],
|
||||
"safeSummary":"one sanitized login event parsed from declared source",
|
||||
"limits":{"maxEvents":256,"maxPayloadBytes":16384,"maxLineBytes":4096,"maxResultBytes":262144}
|
||||
}
|
||||
}
|
||||
}`
|
||||
var request RunJobResultRequest
|
||||
if err := json.Unmarshal([]byte(payload), &request); err != nil {
|
||||
t.Fatalf("unmarshal Run parsed log result: %v", err)
|
||||
}
|
||||
domainRequest := request.ToDomain()
|
||||
result := domainRequest.ExecutionResult.ParsedLogBatch
|
||||
if result == nil || result.ParserKey != "scum-login-log-login-parser" || result.ParserDigest != "sha256:"+strings.Repeat("e", 64) || result.EventCount != 1 || result.Events[0].LogicalEventDigest != "sha256:"+strings.Repeat("3", 64) {
|
||||
t.Fatalf("parsedLogBatch envelope did not parse: %+v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIProviderFromDomainCopiesModels(t *testing.T) {
|
||||
provider := domain.AIProvider{
|
||||
ID: "ai.openai",
|
||||
|
||||
@@ -51,6 +51,13 @@ type SCUMGuardedMutationBoundsDTO struct {
|
||||
MaxAffectedRows int `json:"maxAffectedRows"`
|
||||
}
|
||||
|
||||
type SCUMParsedLogBatchBoundsDTO struct {
|
||||
MaxEvents int `json:"maxEvents"`
|
||||
MaxPayloadBytes int `json:"maxPayloadBytes"`
|
||||
MaxLineBytes int `json:"maxLineBytes"`
|
||||
MaxResultBytes int `json:"maxResultBytes"`
|
||||
}
|
||||
|
||||
type SCUMSchemaProbeRequestDTO struct {
|
||||
RequestID string `json:"requestId"`
|
||||
JobID string `json:"jobId"`
|
||||
@@ -262,6 +269,48 @@ type SCUMGuardedMutationResultDTO struct {
|
||||
Limits SCUMGuardedMutationBoundsDTO `json:"limits"`
|
||||
}
|
||||
|
||||
type SCUMParsedLogCursorDTO struct {
|
||||
SourceIdentityDigest string `json:"sourceIdentityDigest"`
|
||||
StreamGeneration string `json:"streamGeneration"`
|
||||
Sequence uint64 `json:"sequence"`
|
||||
}
|
||||
|
||||
type SCUMParsedLogEventDTO struct {
|
||||
EventType string `json:"eventType"`
|
||||
OccurredAt time.Time `json:"occurredAt"`
|
||||
Cursor SCUMParsedLogCursorDTO `json:"cursor"`
|
||||
LogicalEventDigest string `json:"logicalEventDigest"`
|
||||
EventDigest string `json:"eventDigest"`
|
||||
PayloadDigest string `json:"payloadDigest"`
|
||||
Payload map[string]any `json:"payload,omitempty"`
|
||||
}
|
||||
|
||||
type SCUMParsedLogBatchResultDTO struct {
|
||||
RequestID string `json:"requestId"`
|
||||
JobID string `json:"jobId"`
|
||||
Binding SCUMBindingIdentityDTO `json:"binding"`
|
||||
Status string `json:"status"`
|
||||
SourceKey string `json:"sourceKey"`
|
||||
StreamKey string `json:"streamKey"`
|
||||
ParserKey string `json:"parserKey"`
|
||||
ParserVersion string `json:"parserVersion"`
|
||||
AdapterVersion string `json:"adapterVersion"`
|
||||
AssetDigest string `json:"assetDigest"`
|
||||
ParserDigest string `json:"parserDigest"`
|
||||
ObservedAt time.Time `json:"observedAt"`
|
||||
ResultDigest string `json:"resultDigest"`
|
||||
FirstCursor SCUMParsedLogCursorDTO `json:"firstCursor"`
|
||||
LastCursor SCUMParsedLogCursorDTO `json:"lastCursor"`
|
||||
TailState string `json:"tailState"`
|
||||
PartialLineBuffered bool `json:"partialLineBuffered,omitempty"`
|
||||
Replay bool `json:"replay,omitempty"`
|
||||
EventCount int `json:"eventCount"`
|
||||
Events []SCUMParsedLogEventDTO `json:"events,omitempty"`
|
||||
SafeSummary string `json:"safeSummary,omitempty"`
|
||||
SafeError SCUMSafeErrorDTO `json:"safeError,omitempty"`
|
||||
Limits SCUMParsedLogBatchBoundsDTO `json:"limits"`
|
||||
}
|
||||
|
||||
type SCUMCapabilityGateDTO struct {
|
||||
Capability string `json:"capability"`
|
||||
State string `json:"state"`
|
||||
@@ -430,6 +479,22 @@ func SCUMGuardedMutationResultPtrToDomain(value *SCUMGuardedMutationResultDTO) *
|
||||
return &result
|
||||
}
|
||||
|
||||
func SCUMParsedLogBatchResultToDomain(value SCUMParsedLogBatchResultDTO) domain.SCUMParsedLogBatchResult {
|
||||
events := make([]domain.SCUMParsedLogEvent, len(value.Events))
|
||||
for index, event := range value.Events {
|
||||
events[index] = domain.SCUMParsedLogEvent{EventType: event.EventType, OccurredAt: event.OccurredAt, Cursor: scumParsedLogCursorToDomain(event.Cursor), LogicalEventDigest: event.LogicalEventDigest, EventDigest: event.EventDigest, PayloadDigest: event.PayloadDigest, Payload: domain.CopySCUMValueMap(event.Payload)}
|
||||
}
|
||||
return domain.SCUMParsedLogBatchResult{RequestID: value.RequestID, JobID: value.JobID, Binding: scumBindingIdentityToDomain(value.Binding), Status: domain.SCUMTerminalResultStatus(value.Status), SourceKey: value.SourceKey, StreamKey: value.StreamKey, ParserKey: value.ParserKey, ParserVersion: value.ParserVersion, AdapterVersion: value.AdapterVersion, AssetDigest: value.AssetDigest, ParserDigest: value.ParserDigest, ObservedAt: value.ObservedAt, ResultDigest: value.ResultDigest, FirstCursor: scumParsedLogCursorToDomain(value.FirstCursor), LastCursor: scumParsedLogCursorToDomain(value.LastCursor), TailState: domain.SCUMLogTailState(value.TailState), PartialLineBuffered: value.PartialLineBuffered, Replay: value.Replay, EventCount: value.EventCount, Events: events, SafeSummary: value.SafeSummary, SafeError: domain.SCUMSafeError{Code: domain.SCUMSafeErrorCode(value.SafeError.Code), Message: value.SafeError.Message, Retryable: value.SafeError.Retryable}, Limits: scumParsedLogBatchBoundsToDomain(value.Limits)}
|
||||
}
|
||||
|
||||
func SCUMParsedLogBatchResultPtrToDomain(value *SCUMParsedLogBatchResultDTO) *domain.SCUMParsedLogBatchResult {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
result := SCUMParsedLogBatchResultToDomain(*value)
|
||||
return &result
|
||||
}
|
||||
|
||||
func SCUMSchemaProbeResultFromDomain(value domain.SCUMSchemaProbeResult) SCUMSchemaProbeResultDTO {
|
||||
value = domain.CopySCUMSchemaProbeResult(value)
|
||||
objects := make([]SCUMSchemaObjectEvidenceDTO, len(value.Objects))
|
||||
@@ -496,6 +561,23 @@ func SCUMGuardedMutationResultPtrFromDomain(value *domain.SCUMGuardedMutationRes
|
||||
return &result
|
||||
}
|
||||
|
||||
func SCUMParsedLogBatchResultFromDomain(value domain.SCUMParsedLogBatchResult) SCUMParsedLogBatchResultDTO {
|
||||
value = *domain.CopySCUMParsedLogBatchResultPtr(&value)
|
||||
events := make([]SCUMParsedLogEventDTO, len(value.Events))
|
||||
for index, event := range value.Events {
|
||||
events[index] = SCUMParsedLogEventDTO{EventType: event.EventType, OccurredAt: event.OccurredAt, Cursor: scumParsedLogCursorFromDomain(event.Cursor), LogicalEventDigest: event.LogicalEventDigest, EventDigest: event.EventDigest, PayloadDigest: event.PayloadDigest, Payload: event.Payload}
|
||||
}
|
||||
return SCUMParsedLogBatchResultDTO{RequestID: value.RequestID, JobID: value.JobID, Binding: scumBindingIdentityFromDomain(value.Binding), Status: string(value.Status), SourceKey: value.SourceKey, StreamKey: value.StreamKey, ParserKey: value.ParserKey, ParserVersion: value.ParserVersion, AdapterVersion: value.AdapterVersion, AssetDigest: value.AssetDigest, ParserDigest: value.ParserDigest, ObservedAt: value.ObservedAt, ResultDigest: value.ResultDigest, FirstCursor: scumParsedLogCursorFromDomain(value.FirstCursor), LastCursor: scumParsedLogCursorFromDomain(value.LastCursor), TailState: string(value.TailState), PartialLineBuffered: value.PartialLineBuffered, Replay: value.Replay, EventCount: value.EventCount, Events: events, SafeSummary: value.SafeSummary, SafeError: SCUMSafeErrorDTO{Code: string(value.SafeError.Code), Message: value.SafeError.Message, Retryable: value.SafeError.Retryable}, Limits: scumParsedLogBatchBoundsFromDomain(value.Limits)}
|
||||
}
|
||||
|
||||
func SCUMParsedLogBatchResultPtrFromDomain(value *domain.SCUMParsedLogBatchResult) *SCUMParsedLogBatchResultDTO {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
result := SCUMParsedLogBatchResultFromDomain(*value)
|
||||
return &result
|
||||
}
|
||||
|
||||
func SCUMCapabilityGateFromDomain(value domain.SCUMCapabilityGate) SCUMCapabilityGateDTO {
|
||||
return SCUMCapabilityGateDTO{Capability: string(value.Capability), State: string(value.State), Enabled: value.Enabled, ReasonCode: string(value.ReasonCode), Reason: value.Reason}
|
||||
}
|
||||
@@ -539,3 +621,19 @@ func scumGuardedMutationBoundsToDomain(value SCUMGuardedMutationBoundsDTO) domai
|
||||
func scumGuardedMutationBoundsFromDomain(value domain.SCUMGuardedMutationBounds) SCUMGuardedMutationBoundsDTO {
|
||||
return SCUMGuardedMutationBoundsDTO{MaxPayloadBytes: value.MaxPayloadBytes, TimeoutMS: value.TimeoutMS, BusyTimeoutMS: value.BusyTimeoutMS, MaxReadbackBytes: value.MaxReadbackBytes, MaxAffectedRows: value.MaxAffectedRows}
|
||||
}
|
||||
|
||||
func scumParsedLogCursorToDomain(value SCUMParsedLogCursorDTO) domain.SCUMParsedLogCursor {
|
||||
return domain.SCUMParsedLogCursor{SourceIdentityDigest: value.SourceIdentityDigest, StreamGeneration: value.StreamGeneration, Sequence: value.Sequence}
|
||||
}
|
||||
|
||||
func scumParsedLogCursorFromDomain(value domain.SCUMParsedLogCursor) SCUMParsedLogCursorDTO {
|
||||
return SCUMParsedLogCursorDTO{SourceIdentityDigest: value.SourceIdentityDigest, StreamGeneration: value.StreamGeneration, Sequence: value.Sequence}
|
||||
}
|
||||
|
||||
func scumParsedLogBatchBoundsToDomain(value SCUMParsedLogBatchBoundsDTO) domain.SCUMParsedLogBatchBounds {
|
||||
return domain.SCUMParsedLogBatchBounds{MaxEvents: value.MaxEvents, MaxPayloadBytes: value.MaxPayloadBytes, MaxLineBytes: value.MaxLineBytes, MaxResultBytes: value.MaxResultBytes}
|
||||
}
|
||||
|
||||
func scumParsedLogBatchBoundsFromDomain(value domain.SCUMParsedLogBatchBounds) SCUMParsedLogBatchBoundsDTO {
|
||||
return SCUMParsedLogBatchBoundsDTO{MaxEvents: value.MaxEvents, MaxPayloadBytes: value.MaxPayloadBytes, MaxLineBytes: value.MaxLineBytes, MaxResultBytes: value.MaxResultBytes}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user