package service import ( "strings" "time" "browser.local/platform/domain" "browser.local/platform/validator" ) func (svc *CoreService) projectRemoteAdapterJobResult(job domain.Job, stamp time.Time) error { if !strings.HasPrefix(job.Capability, "remote.") || job.ServerInstanceID == "" || !isTerminalJobState(job.State) { return nil } result := domain.AuditResultSuccess if job.State == domain.JobStateFailed || job.State == domain.JobStateCancelled { result = domain.AuditResultFailed } summary := "remote adapter " + job.Capability + " completed with bounded result reference" if job.State == domain.JobStateFailed { summary = "remote adapter " + job.Capability + " failed or timed out; retry/fencing remained platform-owned" } if job.State == domain.JobStateCancelled { summary = "remote adapter " + job.Capability + " was cancelled before terminal projection" } return svc.recordAuditEvent("run:"+job.RunEndpointID, "remote-adapter.result", "server-instance", job.ServerInstanceID, result, summary) } func (svc *CoreService) projectLifecycleJobResult(job domain.Job, stamp time.Time) error { if job.Capability == domain.JobCapabilityConfigWrite { if job.State != domain.JobStateSucceeded { return svc.recordAuditEvent("run:"+job.RunEndpointID, "config.write.result", "server-instance", job.ServerInstanceID, domain.AuditResultFailed, job.ExecutionResult.AuditSummary) } instance, err := svc.store.ServerInstances().Get(job.ServerInstanceID) if err != nil { return err } instance.ConfigKey = job.TargetKey instance.ConfigContent = job.ExecutionInput.Content instance.ConfigChecksum = job.ExecutionResult.Checksum instance.ConfigVersion = job.ExecutionResult.Version instance.ConfigUpdatedAt = stamp instance.UpdatedAt = stamp if err := validator.ValidateServerInstance(instance); err != nil { return err } if err := svc.store.ServerInstances().Update(instance); err != nil { return err } return svc.recordAuditEvent("run:"+job.RunEndpointID, "config.write.result", "server-instance", instance.ID, domain.AuditResultSuccess, job.ExecutionResult.AuditSummary) } nextState, ok := lifecycleProjectedState(job.Capability, job.State) if !ok || job.ServerInstanceID == "" { return nil } instance, err := svc.store.ServerInstances().Get(job.ServerInstanceID) if err != nil { return err } instance.State = nextState instance.UpdatedAt = stamp if err := validator.ValidateServerInstance(instance); err != nil { return err } if err := svc.store.ServerInstances().Update(instance); err != nil { return err } auditResult := domain.AuditResultSuccess if job.State == domain.JobStateFailed || job.State == domain.JobStateCancelled { auditResult = domain.AuditResultFailed } return svc.recordAuditEvent("run:"+job.RunEndpointID, "lifecycle.result", "server-instance", instance.ID, auditResult, job.Progress.Message) } func lifecycleProjectedState(capability string, jobState domain.JobState) (domain.ServerInstanceState, bool) { if capability != domain.LifecycleCapabilityInstall && capability != domain.LifecycleCapabilityStart && capability != domain.LifecycleCapabilityStop { return "", false } if jobState == domain.JobStateFailed || jobState == domain.JobStateCancelled { return domain.ServerInstanceStateFailed, true } if jobState != domain.JobStateSucceeded { return "", false } switch capability { case domain.LifecycleCapabilityInstall: return domain.ServerInstanceStateReady, true case domain.LifecycleCapabilityStart: return domain.ServerInstanceStateRunning, true case domain.LifecycleCapabilityStop: return domain.ServerInstanceStateStopped, true default: return "", false } }