Files
narratio/internal/stage/stage.go

141 lines
4.3 KiB
Go

package stage
import (
"context"
"log/slog"
"strings"
"unicode/utf8"
"gitea.maximumdirect.net/eric/narratio/internal/adapters/audita"
"gitea.maximumdirect.net/eric/narratio/internal/adapters/notarius"
"gitea.maximumdirect.net/eric/narratio/internal/adapters/notify"
"gitea.maximumdirect.net/eric/narratio/internal/adapters/scriptorium"
"gitea.maximumdirect.net/eric/narratio/internal/adapters/seriatim"
"gitea.maximumdirect.net/eric/narratio/internal/adapters/storage"
"gitea.maximumdirect.net/eric/narratio/internal/adapters/whisperx"
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
"gitea.maximumdirect.net/eric/narratio/internal/config"
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
)
// Env is the shared dependency container visible to stages.
type Env struct {
Config *config.Config
SelectedArtifactKeys []string
EffectiveArtifacts artifacts.EffectiveArtifactSet
ArtifactStore artifacts.Store
ManifestStore manifest.Store
Logger *slog.Logger
Force bool
WhisperX whisperx.Client
Seriatim seriatim.Runner
Audita audita.Runner
Notarius notarius.Runner
Scriptorium scriptorium.Runner
ObjectStore storage.ObjectStore
Notifier notify.Sender
// RevalidatePublishLocks returns the effective lock set immediately before a
// publish commit selects a new remote snapshot.
RevalidatePublishLocks func(context.Context) ([]config.PublishLockRule, error)
}
// Stage is the pipeline unit contract.
type Stage interface {
Name() string
Run(ctx context.Context, env *Env, m *manifest.Manifest) (*StageResult, error)
}
const maxResumeReasonLength = 512
// ResumeValidation reports whether a previously succeeded stage can be reused.
type ResumeValidation struct {
Resumable bool
Reason string
Analyze *AnalyzeResumeSummary
}
// AnalyzeResumeSummary describes the artifact-level decision behind an
// aggregate analyze resume result.
type AnalyzeResumeSummary struct {
ExplicitTargets []string
PrerequisiteWork []AnalyzeResumeArtifact
ExecutionOrder []AnalyzeResumeArtifact
ReusedCurrent []AnalyzeResumeArtifact
}
// AnalyzeResumeArtifact is one deterministic artifact-level plan entry.
type AnalyzeResumeArtifact struct {
Key string
Family string
CharacterID string
Role string
Reason string
Forced bool
}
// Normalized returns a result with a bounded reason and no reason on success.
func (r ResumeValidation) Normalized() ResumeValidation {
if r.Resumable {
r.Reason = ""
return r
}
normalized := NonResumable(r.Reason)
normalized.Analyze = r.Analyze
return normalized
}
// ResumeValidator is implemented by stages that validate persisted success before reuse.
type ResumeValidator interface {
ValidateResume(ctx context.Context, env *Env, m *manifest.Manifest) (ResumeValidation, error)
}
// Resumable reports a successful resume validation.
func Resumable() ResumeValidation {
return ResumeValidation{Resumable: true}
}
// NonResumable reports a bounded reason that persisted success must be rerun.
func NonResumable(reason string) ResumeValidation {
reason = strings.TrimSpace(reason)
if reason == "" {
reason = "persisted stage result is not reusable"
}
if len(reason) > maxResumeReasonLength {
cutoff := maxResumeReasonLength
for cutoff > 0 && !utf8.ValidString(reason[:cutoff]) {
cutoff--
}
reason = reason[:cutoff]
}
return ResumeValidation{Reason: reason}
}
// StageDisposition describes the outcome of a stage that returned without an error.
type StageDisposition string
const (
// StageDispositionSucceeded is the zero value so existing stages remain successful.
StageDispositionSucceeded StageDisposition = ""
StageDispositionSkipped StageDisposition = "skipped"
)
// StageResult is the declared output of a stage execution.
type StageResult struct {
Disposition StageDisposition
SkipReason string
Outputs []artifacts.Ref
Logs []string
GeneratedConfigs []string
Metadata map[string]any
AnalyzeState *AnalyzeStateProjection
}
// AnalyzeStateProjection carries analyze-owned reconciled session authority and
// the invocation subset evaluated by the current run.
type AnalyzeStateProjection struct {
Session map[string]manifest.AnalyzeArtifactRecord
Invocation map[string]manifest.AnalyzeArtifactRecord
}