diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 049b405..788d997 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -360,7 +360,7 @@ preserving every durable transition and failure boundary. ## Stage 5 — Isolate Finalization And Simplify The Lifecycle Owner -**Status: Pending** +**Status: Completed** ### Goal diff --git a/internal/app/runner.go b/internal/app/runner.go index fd1fea8..1c25c36 100644 --- a/internal/app/runner.go +++ b/internal/app/runner.go @@ -71,10 +71,6 @@ func executePlanWithDependencies(ctx context.Context, cfg *config.Config, plan B return nil, err } - env := runtime.env - manifestPath := runtime.manifestPath - m := runtime.manifest - runID := runtime.identity.RunID for _, selected := range runtime.stages { runtime.stageNames = append(runtime.stageNames, selected.Name()) outcome, err := executeSelectedStage(runtime, selected) @@ -88,25 +84,7 @@ func executePlanWithDependencies(ctx context.Context, cfg *config.Config, plan B runtime.skipped = append(runtime.skipped, selected.Name()) } } - - if containsStage(runtime.executed, "publish") { - if err := runPostPublishCleanup(ctx, env, manifestPath, m, runtime.executed, runtime.dependencies.removeScopedDir); err != nil { - return nil, persistPostPublishCleanupFailure( - ctx, env.ManifestStore, manifestPath, m, - fmt.Errorf("post-publish cleanup incomplete: %w", err), - runtime.dependencies.now(), - ) - } - } - - return &RunSummary{ - SessionID: cfg.Session.SessionID, - RunID: runID, - ManifestPath: manifestPath, - StageNames: runtime.stageNames, - Executed: runtime.executed, - Skipped: runtime.skipped, - }, nil + return finalizeInvocation(runtime) } func stagesContainAny(stages []stage.Stage, names ...string) bool { @@ -125,27 +103,6 @@ func stagesContainAny(stages []stage.Stage, names ...string) bool { return false } -func persistPostPublishCleanupFailure( - ctx context.Context, - sessionStore manifest.Store, - sessionManifestPath string, - sessionManifest *manifest.Manifest, - operationErr error, - at time.Time, -) error { - if operationErr == nil { - return nil - } - if sessionManifest != nil { - sessionManifest.RecordFailure(at, operationErr.Error()) - } - // Cleanup may have deliberately removed the selected run directory. The - // committed remote snapshot remains authoritative, while the session - // manifest records the outstanding local cleanup for the next invocation. - sessionErr := sessionStore.Save(ctx, sessionManifestPath, sessionManifest) - return errors.Join(operationErr, wrapTerminalPersistenceError("save terminal session manifest", sessionErr)) -} - func persistSessionTerminalFailure( ctx context.Context, sessionStore manifest.Store, diff --git a/internal/app/runner_finalization.go b/internal/app/runner_finalization.go new file mode 100644 index 0000000..4689eb4 --- /dev/null +++ b/internal/app/runner_finalization.go @@ -0,0 +1,62 @@ +package app + +import ( + "context" + "errors" + "fmt" + "time" + + "gitea.maximumdirect.net/eric/narratio/internal/manifest" +) + +func finalizeInvocation(runtime *invocationRuntime) (*RunSummary, error) { + if containsStage(runtime.executed, "publish") { + if err := runPostPublishCleanup( + runtime.ctx, + runtime.env, + runtime.manifestPath, + runtime.manifest, + runtime.executed, + runtime.dependencies.removeScopedDir, + ); err != nil { + return nil, persistPostPublishCleanupFailure( + runtime.ctx, + runtime.manifestStore, + runtime.manifestPath, + runtime.manifest, + fmt.Errorf("post-publish cleanup incomplete: %w", err), + runtime.dependencies.now(), + ) + } + } + + return &RunSummary{ + SessionID: runtime.identity.SessionID, + RunID: runtime.identity.RunID, + ManifestPath: runtime.manifestPath, + StageNames: runtime.stageNames, + Executed: runtime.executed, + Skipped: runtime.skipped, + }, nil +} + +func persistPostPublishCleanupFailure( + ctx context.Context, + sessionStore manifest.Store, + sessionManifestPath string, + sessionManifest *manifest.Manifest, + operationErr error, + at time.Time, +) error { + if operationErr == nil { + return nil + } + if sessionManifest != nil { + sessionManifest.RecordFailure(at, operationErr.Error()) + } + // Cleanup may have deliberately removed the selected run directory. The + // committed remote snapshot remains authoritative, while the session + // manifest records the outstanding local cleanup for the next invocation. + sessionErr := sessionStore.Save(ctx, sessionManifestPath, sessionManifest) + return errors.Join(operationErr, wrapTerminalPersistenceError("save terminal session manifest", sessionErr)) +}