Complete analyze checkpoint recovery

This commit is contained in:
2026-09-04 16:10:54 +00:00
parent 74977b4064
commit 7febccf550
11 changed files with 416 additions and 490 deletions

View File

@@ -48,10 +48,55 @@ func TestAnalyzeAdapterFailureRecordsCompletedAndFailedArtifacts(t *testing.T) {
}
}
}
manifestPath := artifacts.SessionManifestPathForCampaign(
env.Config.Pipeline.Workspace.Root, m.Campaign, m.SessionID,
)
durable, loadErr := env.ManifestStore.Load(context.Background(), manifestPath)
if loadErr != nil {
t.Fatalf("load durable manifest: %v", loadErr)
}
for index, key := range keys {
record, exists := durable.Stages["analyze"].AnalyzeArtifacts[key]
if index < failAt-1 {
if !exists || record.Status != manifest.AnalyzeArtifactCurrent {
t.Fatalf("durable completed %s = %#v", key, record)
}
} else if exists && record.Status == manifest.AnalyzeArtifactCurrent {
t.Fatalf("durable uncommitted %s = %#v", key, record)
}
}
})
}
}
func TestAnalyzeCancellationPreservesEarlierCheckpoint(t *testing.T) {
env, m, _ := setupAnalyzeEnv(t)
env.Config.Pipeline.Scriptorium.Artifacts = independentAnalyzeArtifacts()
env.Scriptorium = &indexedAnalyzeFailureRunner{FailAt: 2, Err: context.Canceled}
_, err := (analyzeStage{}).Run(context.Background(), env, m)
if !errors.Is(err, context.Canceled) {
t.Fatalf("Run() error = %v, want cancellation", err)
}
manifestPath := artifacts.SessionManifestPathForCampaign(
env.Config.Pipeline.Workspace.Root, m.Campaign, m.SessionID,
)
durable, loadErr := env.ManifestStore.Load(context.Background(), manifestPath)
if loadErr != nil {
t.Fatalf("load durable manifest: %v", loadErr)
}
if got := durable.Stages["analyze"].AnalyzeArtifacts["alpha"]; got.Status != manifest.AnalyzeArtifactCurrent {
t.Fatalf("durable alpha = %#v, want current", got)
}
if _, exists := durable.Stages["analyze"].AnalyzeArtifacts["beta"]; exists {
t.Fatalf("durable beta should be absent after cancellation: %#v", durable.Stages["analyze"].AnalyzeArtifacts["beta"])
}
if got := m.Stages["analyze"].AnalyzeArtifacts["beta"]; got.Status != manifest.AnalyzeArtifactFailed || got.Output != nil {
t.Fatalf("live beta = %#v, want failed without output", got)
}
}
func TestAnalyzeMiddleFailureStalesDependentsAndPreservesUnrelatedCurrent(t *testing.T) {
env, m, _ := setupAnalyzeEnv(t)
env.Config.Pipeline.Scriptorium.Artifacts = chainedAnalyzeArtifacts()

View File

@@ -39,7 +39,7 @@ func TestAnalyzeReusesCurrentArtifactWithoutScriptorium(t *testing.T) {
}
}
func TestAnalyzePartialForcePreservesUnrelatedCurrentRecord(t *testing.T) {
func TestAnalyzeIdenticalReplacementPreservesUnselectedDependent(t *testing.T) {
env, m, fake := setupAnalyzeEnv(t)
paths := sessionPathsForEnv(env, m.SessionID)
writeAnalyzeFile(t, filepath.Join(paths.TranscriptsDir, "final.trimmed.json"), `{"segments":[]}`)

View File

@@ -129,11 +129,4 @@ type StageResult struct {
Logs []string
GeneratedConfigs []string
Metadata map[string]any
AnalyzeState *AnalyzeStateProjection
}
// AnalyzeStateProjection carries the reconciled session authority proposed for
// the runner's existing end-of-stage session-manifest save.
type AnalyzeStateProjection struct {
Session map[string]manifest.AnalyzeArtifactRecord
}