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 } if plan := job.ExecutionInput.ServerDeploymentPlan; plan != nil { projection := domain.CopyServerDeploymentProjection(instance.DeploymentProjection) projection.Operation = plan.Operation projection.TemplateKey = plan.TemplateKey projection.TemplateVersion = plan.TemplateVersion projection.UpdatedAt = stamp if evidence := job.ExecutionResult.ServerDeploymentEvidence; evidence != nil { projection.State = "verified" projection.PreflightState = evidence.PreflightState projection.DiscoveryState = evidence.DiscoveryState projection.MappingState = evidence.MappingState projection.VerificationState = evidence.VerificationState projection.DiscoveredFacts = domain.CopyStringMap(evidence.DiscoveredFacts) projection.MappingResults = domain.CopyStringMap(evidence.MappingResults) projection.VerificationResults = domain.CopyStringMap(evidence.VerificationResults) projection.FailureCode = evidence.FailureCode } if job.State == domain.JobStateFailed || job.State == domain.JobStateCancelled { projection.State = "failed" if projection.FailureCode == "" && job.State == domain.JobStateCancelled { projection.FailureCode = "cancelled" } } instance.DeploymentProjection = projection } 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 (svc *CoreService) projectServerDeploymentProgress(job domain.Job, stamp time.Time) error { plan := job.ExecutionInput.ServerDeploymentPlan if plan == nil || job.ServerInstanceID == "" { return nil } instance, err := svc.store.ServerInstances().Get(job.ServerInstanceID) if err != nil { return err } projection := domain.CopyServerDeploymentProjection(instance.DeploymentProjection) projection.State = "running" projection.Operation = plan.Operation projection.TemplateKey = plan.TemplateKey projection.TemplateVersion = plan.TemplateVersion switch job.Progress.Phase { case "preflight": projection.PreflightState = "running" case "scan", "discover", "discovery": projection.DiscoveryState = "running" case "install", "configure", "mapping": projection.MappingState = "running" case "start", "health": projection.VerificationState = "running" } projection.UpdatedAt = stamp instance.DeploymentProjection = projection instance.UpdatedAt = stamp return svc.store.ServerInstances().Update(instance) } 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 } }