From 3722e572dfd1f8ddd7203063c4dad55e69ed6fb6 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 22 Sep 2026 15:09:29 +0000 Subject: [PATCH] Delegate analyze checkpoints to shared persistence --- docs/roadmap/implementation.md | 12 ++++++++-- internal/stage/analyze_execution_state.go | 28 +++++++++++------------ 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 51edbd9..06c6d5a 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,6 +1,6 @@ # Implementation Plan: Shared Stage Checkpoint Mechanics -Status: Implementation in progress; Stage 1 complete. +Status: Implementation in progress; Stages 1-2 complete. ## Scope and Authority @@ -193,7 +193,15 @@ stage behavior. Temporary copying overlap is removed in Stage 3. ## Stage 2: Migrate Analyze -Status: Pending. +Status: Complete. + +Completion marker (2026-09-22): Replaced analyze's local candidate-copy, +save, and promotion choreography with the shared checkpoint operation while +retaining its existing-record requirement, typed validation, path resolution, +and post-save working and committed collection refresh. Existing recovery, +selection, invalidation, and rollback coverage required no duplication. +Verified with `go test ./internal/stage ./internal/app ./internal/manifest`, +`go test -race ./internal/stage ./internal/app`, and `git diff --check`. Preparation: `internal/stage/analyze_execution_state.go`, `analyze_checkpoint_test.go`, `analyze_failure_test.go`, diff --git a/internal/stage/analyze_execution_state.go b/internal/stage/analyze_execution_state.go index a167451..32d003a 100644 --- a/internal/stage/analyze_execution_state.go +++ b/internal/stage/analyze_execution_state.go @@ -136,28 +136,26 @@ func (state *analyzeExecutionState) checkpointCollection( return fmt.Errorf("validate analyze artifact collection: %w", err) } - candidate := *m - candidate.Stages = make(map[string]*manifest.StageRecord, len(m.Stages)) - for name, record := range m.Stages { - candidate.Stages[name] = record - } - analyzeRecord := *m.Stages["analyze"] - analyzeRecord.AnalyzeStateVersion = manifest.AnalyzeStateContractVersion - analyzeRecord.AnalyzeArtifacts = manifest.CloneAnalyzeArtifactCollection(records) - candidate.Stages["analyze"] = &analyzeRecord - manifestPath := artifacts.SessionManifestPathForCampaign( state.execution.Paths.WorkspaceRoot, m.Campaign, m.SessionID, ) - if err := state.execution.Env.ManifestStore.Save(ctx, manifestPath, &candidate); err != nil { - return fmt.Errorf("save session manifest %q: %w", manifestPath, err) + if err := checkpointStageCollection( + ctx, + state.execution.Env.ManifestStore, + manifestPath, + m, + "analyze", + func(record *manifest.StageRecord) error { + record.AnalyzeStateVersion = manifest.AnalyzeStateContractVersion + record.AnalyzeArtifacts = manifest.CloneAnalyzeArtifactCollection(records) + return nil + }, + ); err != nil { + return err } liveRecord := m.Stages["analyze"] - liveRecord.AnalyzeStateVersion = candidate.Stages["analyze"].AnalyzeStateVersion - liveRecord.AnalyzeArtifacts = manifest.CloneAnalyzeArtifactCollection(candidate.Stages["analyze"].AnalyzeArtifacts) - m.UpdatedAt = candidate.UpdatedAt state.records = manifest.CloneAnalyzeArtifactCollection(liveRecord.AnalyzeArtifacts) state.committedRecords = manifest.CloneAnalyzeArtifactCollection(liveRecord.AnalyzeArtifacts) return nil