Unify pre-execution stage decisions

This commit is contained in:
2026-09-04 01:01:46 +00:00
parent 9331373245
commit ca1b694aa9
5 changed files with 297 additions and 105 deletions

View File

@@ -244,9 +244,9 @@ func executePlan(ctx context.Context, cfg *config.Config, plan BoundedPlan, opts
fmt.Errorf("fingerprint semantic configuration for stage %q: %w", s.Name(), err),
)
}
action := decideStageAction(s, m, opts.Force)
decision := decideStageExecution(recordedStageStatus(m, s.Name()), opts.Force, nil)
if action == stageActionSkip {
if decision.action == stageActionSkip {
validation, err := evaluateStageResume(ctx, s, stageEnv, m, semanticConfig)
if err != nil {
return nil, persistSessionTerminalFailure(
@@ -254,12 +254,10 @@ func executePlan(ctx context.Context, cfg *config.Config, plan BoundedPlan, opts
fmt.Errorf("validate resume for stage %q: %w", s.Name(), err),
)
}
if validation != nil && !validation.Resumable {
decision = decideStageExecution(recordedStageStatus(m, s.Name()), opts.Force, validation)
if decision.markSelectedStale {
staleAt := nowUTC()
m.MarkStageStale(s.Name(), staleAt, validation.Reason)
if _, err := invalidateDependentSucceededStagesWithReason(
m, s.Name(), staleAt, staleReasonNotResumable,
); err != nil {
if _, err := applyPreExecutionDecision(m, s.Name(), decision, staleAt); err != nil {
return nil, persistSessionTerminalFailure(
ctx, env.ManifestStore, manifestPath, m,
fmt.Errorf("invalidate dependents after resume validation for stage %q: %w", s.Name(), err),
@@ -271,12 +269,11 @@ func executePlan(ctx context.Context, cfg *config.Config, plan BoundedPlan, opts
fmt.Errorf("save manifest after resume validation for stage %q: %w", s.Name(), err),
)
}
env.Logger.Info("stage result is not resumable", "stage", s.Name(), "reason", validation.Reason)
action = stageActionRun
env.Logger.Info("stage result is not resumable", "stage", s.Name(), "reason", decision.selectedStaleReason)
}
}
if action == stageActionSkip {
if decision.action == stageActionSkip {
skipped = append(skipped, s.Name())
env.Logger.Info("skipping stage", "stage", s.Name(), "reason", "already_succeeded", "force", opts.Force)
continue
@@ -286,14 +283,11 @@ func executePlan(ctx context.Context, cfg *config.Config, plan BoundedPlan, opts
priorAnalyzeState := captureAnalyzeState(m, s.Name())
now := nowUTC()
m.MarkStageRunning(s.Name(), now)
if opts.Force {
if _, err := invalidateDependentSucceededStagesWithReason(m, s.Name(), now, staleReasonForcedReplacement); err != nil {
return nil, persistSessionTerminalFailure(
ctx, env.ManifestStore, manifestPath, m,
fmt.Errorf("invalidate dependents before forced stage %q: %w", s.Name(), err),
)
}
if _, err := beginStageAttempt(m, s.Name(), decision, now); err != nil {
return nil, persistSessionTerminalFailure(
ctx, env.ManifestStore, manifestPath, m,
fmt.Errorf("invalidate dependents before forced stage %q: %w", s.Name(), err),
)
}
env.Logger.Info("starting stage", "stage", s.Name())
if err := env.ManifestStore.Save(ctx, manifestPath, m); err != nil {
@@ -340,16 +334,11 @@ func executePlan(ctx context.Context, cfg *config.Config, plan BoundedPlan, opts
if result != nil && result.Disposition == stage.StageDispositionSkipped {
skipped = append(skipped, s.Name())
skippedAt := nowUTC()
m.MarkStageSkipped(s.Name(), skippedAt, result.SkipReason)
applyStageResultToManifest(m, s.Name(), result)
setSessionStageSemanticConfig(m, s.Name(), semanticConfig)
if !priorOutcome.isSameSelfSkip(result.SkipReason) {
if _, err := invalidateDependentSucceededStagesWithReason(m, s.Name(), skippedAt, staleReasonSelfSkip); err != nil {
return nil, persistSessionTerminalFailure(
ctx, env.ManifestStore, manifestPath, m,
fmt.Errorf("invalidate dependents after stage %q self-skip: %w", s.Name(), err),
)
}
if _, err := applyCompletedStageOutcome(m, s.Name(), result, nil, semanticConfig, priorOutcome, skippedAt); err != nil {
return nil, persistSessionTerminalFailure(
ctx, env.ManifestStore, manifestPath, m,
fmt.Errorf("invalidate dependents after stage %q self-skip: %w", s.Name(), err),
)
}
if err := env.ManifestStore.Save(ctx, manifestPath, m); err != nil {
return nil, persistSessionTerminalFailure(
@@ -367,18 +356,13 @@ func executePlan(ctx context.Context, cfg *config.Config, plan BoundedPlan, opts
sessionOutputs = analyzeProjectionOutputs(analyzeProjection.session)
}
succeededAt := nowUTC()
m.MarkStageSucceeded(s.Name(), succeededAt, sessionOutputs)
applyAnalyzeProjection(m, analyzeProjection)
applyStageResultToManifest(m, s.Name(), result)
setSessionStageSemanticConfig(m, s.Name(), semanticConfig)
if !priorOutcome.exists || priorOutcome.status != manifest.StatusSucceeded {
if _, err := invalidateDependentSucceededStagesWithReason(m, s.Name(), succeededAt, staleReasonChangedResult); err != nil {
return nil, persistSessionTerminalFailure(
ctx, env.ManifestStore, manifestPath, m,
fmt.Errorf("invalidate dependents after changed stage %q result: %w", s.Name(), err),
)
}
if _, err := applyCompletedStageOutcome(m, s.Name(), result, sessionOutputs, semanticConfig, priorOutcome, succeededAt); err != nil {
return nil, persistSessionTerminalFailure(
ctx, env.ManifestStore, manifestPath, m,
fmt.Errorf("invalidate dependents after changed stage %q result: %w", s.Name(), err),
)
}
applyAnalyzeProjection(m, analyzeProjection)
if err := env.ManifestStore.Save(ctx, manifestPath, m); err != nil {
operationErr := fmt.Errorf("save manifest after stage %q: %w", s.Name(), err)