Harden pipeline state and plan release upgrades

This commit is contained in:
2026-08-30 18:51:20 +00:00
parent 3da97ca50c
commit c812fe3655
18 changed files with 1381 additions and 1237 deletions

View File

@@ -125,7 +125,17 @@ func TestRunArtifactsWithSucceededAnalyzeSkipsUnlessForced(t *testing.T) {
manifestPath := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json")
store := &manifest.LocalStore{}
seed := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
if err := RunStage(
context.Background(),
[]string{"prepare", "2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath},
&bytes.Buffer{},
); err != nil {
t.Fatalf("seed prepare stage: %v", err)
}
seed, err := store.Load(context.Background(), manifestPath)
if err != nil {
t.Fatalf("load prepared manifest: %v", err)
}
for _, stageName := range []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim", "render", "analyze", "publish", "notify"} {
seed.MarkStageSucceeded(stageName, time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
}
@@ -136,7 +146,7 @@ func TestRunArtifactsWithSucceededAnalyzeSkipsUnlessForced(t *testing.T) {
}
var out bytes.Buffer
err := Run(
err = Run(
context.Background(),
[]string{"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath, "--artifacts", "session_recap"},
&out,

View File

@@ -549,6 +549,17 @@ inputs:
return pipelinePath, campaignPath, sessionPath
}
func materializePrepareResumeFixture(t *testing.T, pipelinePath, campaignPath, sessionPath string) {
t.Helper()
if err := RunStage(
context.Background(),
[]string{"prepare", "2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath},
&bytes.Buffer{},
); err != nil {
t.Fatalf("materialize prepare resume fixture: %v", err)
}
}
func writeAppTestCampaignConfig(t *testing.T, dir string) string {
t.Helper()
campaignPath := filepath.Join(dir, "campaign.yml")

View File

@@ -61,8 +61,11 @@ func TestPlanShowsRunAndSkipFromManifest(t *testing.T) {
manifestPath := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json")
store := &manifest.LocalStore{}
m := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
m.MarkStageSucceeded("prepare", time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
materializePrepareResumeFixture(t, pipelinePath, campaignPath, sessionPath)
m, err := store.Load(context.Background(), manifestPath)
if err != nil {
t.Fatalf("load prepared manifest: %v", err)
}
m.MarkStageSucceeded("transcribe", time.Date(2026, 5, 3, 10, 2, 0, 0, time.UTC), nil)
seedCurrentSemanticEvidence(t, loadConfigForSemanticEvidence(t, pipelinePath, campaignPath, sessionPath), m, "prepare", "transcribe")
if err := store.Save(context.Background(), manifestPath, m); err != nil {

View File

@@ -19,8 +19,11 @@ func TestRunContinuesAfterCompletedStages(t *testing.T) {
manifestPath := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json")
store := &manifest.LocalStore{}
m := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
m.MarkStageSucceeded("prepare", time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
materializePrepareResumeFixture(t, pipelinePath, campaignPath, sessionPath)
m, err := store.Load(context.Background(), manifestPath)
if err != nil {
t.Fatalf("load prepared manifest: %v", err)
}
m.MarkStageSucceeded("transcribe", time.Date(2026, 5, 3, 10, 2, 0, 0, time.UTC), nil)
seedCurrentSemanticEvidence(t, loadConfigForSemanticEvidence(t, pipelinePath, campaignPath, sessionPath), m, "prepare", "transcribe")
if err := store.Save(context.Background(), manifestPath, m); err != nil {
@@ -28,12 +31,9 @@ func TestRunContinuesAfterCompletedStages(t *testing.T) {
}
workRoot := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03")
mustWriteTestFile(t, filepath.Join(workRoot, "transcripts", "raw", "alice.json"), `{"segments":[]}`)
mustWriteTestFile(t, filepath.Join(workRoot, "inputs", "speakers.yml"), "match:\n - speaker: Alice\n match: [\"alice\"]\n")
mustWriteTestFile(t, filepath.Join(workRoot, "inputs", "autocorrect.yml"), "[]\n")
mustWriteTestFile(t, filepath.Join(workRoot, "inputs", "glossary.yml"), "terms: []\n")
var out bytes.Buffer
err := Run(context.Background(), []string{"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath}, &out)
err = Run(context.Background(), []string{"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath}, &out)
if err != nil {
t.Fatalf("Run() error = %v", err)
}
@@ -56,7 +56,11 @@ func TestRunNoRemainingStagesRecordsSkippedStages(t *testing.T) {
manifestPath := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json")
store := &manifest.LocalStore{}
m := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
materializePrepareResumeFixture(t, pipelinePath, campaignPath, sessionPath)
m, err := store.Load(context.Background(), manifestPath)
if err != nil {
t.Fatalf("load prepared manifest: %v", err)
}
for _, name := range []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim", "render", "analyze", "publish", "notify"} {
m.MarkStageSucceeded(name, time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
}
@@ -67,7 +71,7 @@ func TestRunNoRemainingStagesRecordsSkippedStages(t *testing.T) {
}
var out bytes.Buffer
err := Run(context.Background(), []string{"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath}, &out)
err = Run(context.Background(), []string{"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath}, &out)
if err != nil {
t.Fatalf("Run() error = %v", err)
}
@@ -204,10 +208,13 @@ func TestRunStageForceMarksDownstreamStaleAndRunContinuesFromStale(t *testing.T)
manifestPath := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json")
workRoot := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03")
mustWriteTestFile(t, filepath.Join(workRoot, "transcripts", "base.json"), `{"segments":[]}`)
mustWriteTestFile(t, filepath.Join(workRoot, "inputs", "glossary.yml"), "terms: []\n")
store := &manifest.LocalStore{}
seed := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
materializePrepareResumeFixture(t, pipelinePath, campaignPath, sessionPath)
seed, err := store.Load(context.Background(), manifestPath)
if err != nil {
t.Fatalf("load prepared manifest: %v", err)
}
for _, name := range []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim", "extract", "render", "analyze", "publish", "notify"} {
seed.MarkStageSucceeded(name, time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
}
@@ -217,7 +224,7 @@ func TestRunStageForceMarksDownstreamStaleAndRunContinuesFromStale(t *testing.T)
}
var out bytes.Buffer
err := RunStage(context.Background(), []string{"polish", "2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath, "--force"}, &out)
err = RunStage(context.Background(), []string{"polish", "2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath, "--force"}, &out)
if err != nil {
t.Fatalf("RunStage(force) error = %v", err)
}