Replace normalize stage with trim scaffold
This commit is contained in:
@@ -17,7 +17,7 @@ Implemented now:
|
||||
|
||||
Not implemented yet:
|
||||
|
||||
- real `normalize` behavior
|
||||
- real `trim` behavior
|
||||
- real `archive` behavior
|
||||
- real `notify` behavior
|
||||
- additional analyze artifacts beyond `session_recap`
|
||||
|
||||
@@ -23,7 +23,7 @@ Implemented:
|
||||
|
||||
Still placeholder/future:
|
||||
|
||||
- `normalize` stage behavior
|
||||
- `trim` stage behavior
|
||||
- `archive` stage behavior
|
||||
- `notify` stage behavior
|
||||
- additional Scriptorium artifact types beyond `session_recap`
|
||||
@@ -36,9 +36,9 @@ Canonical stage order:
|
||||
|
||||
1. `prepare`
|
||||
2. `transcribe`
|
||||
3. `normalize`
|
||||
4. `merge`
|
||||
5. `polish`
|
||||
3. `merge`
|
||||
4. `polish`
|
||||
5. `trim`
|
||||
6. `analyze`
|
||||
7. `archive`
|
||||
8. `notify`
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestExecuteValidCommands(t *testing.T) {
|
||||
wantOut string
|
||||
}{
|
||||
{name: "run", args: []string{"run", "--config", pipelinePath, "--session", sessionPath}, wantOut: "narratio run: session 2026-05-03; executed=8 skipped=0; manifest="},
|
||||
{name: "plan", args: []string{"plan", "--config", pipelinePath, "--session", sessionPath}, wantOut: "prepare: skip\ntranscribe: skip\nnormalize: skip\nmerge: skip\npolish: skip\nanalyze: skip\narchive: skip\nnotify: skip"},
|
||||
{name: "plan", args: []string{"plan", "--config", pipelinePath, "--session", sessionPath}, wantOut: "prepare: skip\ntranscribe: skip\nmerge: skip\npolish: skip\ntrim: skip\nanalyze: skip\narchive: skip\nnotify: skip"},
|
||||
{name: "status", args: []string{"status", "--manifest", manifestPath}, wantOut: "session_id: 2026-05-03"},
|
||||
{name: "resume", args: []string{"resume", "--config", pipelinePath, "--session", sessionPath}, wantOut: "narratio resume: session 2026-05-03 has no remaining stages"},
|
||||
{name: "run-stage", args: []string{"run-stage", "--config", pipelinePath, "--session", sessionPath, "polish"}, wantOut: "narratio run-stage: stage=polish executed=0 skipped=1 force=false; manifest="},
|
||||
@@ -106,6 +106,22 @@ func TestExecuteRunStageUnknownFails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteRunStageNormalizeIsRejected(t *testing.T) {
|
||||
workspaceRoot := t.TempDir()
|
||||
pipelinePath, sessionPath := writeValidConfigFiles(t, workspaceRoot, "https://example.com/transcribe")
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
code := Execute([]string{"run-stage", "--config", pipelinePath, "--session", sessionPath, "normalize"}, &stdout, &stderr)
|
||||
if code == 0 {
|
||||
t.Fatal("exit code = 0, want non-zero")
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "unknown stage") {
|
||||
t.Fatalf("stderr = %q, want unknown stage error", stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteRunStageTranscribeUsesConfiguredWhisperXServer(t *testing.T) {
|
||||
workspaceRoot := t.TempDir()
|
||||
var serverCalls int
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestPlanCreatesAndReusesWorkdir(t *testing.T) {
|
||||
if !strings.Contains(got, "narratio plan: workdir prepared at") {
|
||||
t.Fatalf("first output = %q, want workdir prepared", got)
|
||||
}
|
||||
for _, name := range []string{"prepare", "transcribe", "normalize", "merge", "polish", "analyze", "archive", "notify"} {
|
||||
for _, name := range []string{"prepare", "transcribe", "merge", "polish", "trim", "analyze", "archive", "notify"} {
|
||||
if !strings.Contains(got, name+": run") {
|
||||
t.Fatalf("first output = %q, missing stage %q", got, name)
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func TestPlanCreatesAndReusesWorkdir(t *testing.T) {
|
||||
filepath.Join(sessionWorkdir, "inputs"),
|
||||
filepath.Join(sessionWorkdir, "audio"),
|
||||
filepath.Join(sessionWorkdir, "transcripts", "raw"),
|
||||
filepath.Join(sessionWorkdir, "transcripts", "normalized"),
|
||||
filepath.Join(sessionWorkdir, "transcripts", "trimmed"),
|
||||
filepath.Join(sessionWorkdir, "artifacts"),
|
||||
filepath.Join(sessionWorkdir, "config"),
|
||||
filepath.Join(sessionWorkdir, "logs"),
|
||||
@@ -81,8 +81,8 @@ func TestPlanShowsRunAndSkipFromManifest(t *testing.T) {
|
||||
if !strings.Contains(got, "prepare: skip") || !strings.Contains(got, "transcribe: skip") {
|
||||
t.Fatalf("output = %q, want prepare/transcribe skipped", got)
|
||||
}
|
||||
if !strings.Contains(got, "normalize: run") {
|
||||
t.Fatalf("output = %q, want normalize run", got)
|
||||
if !strings.Contains(got, "trim: run") {
|
||||
t.Fatalf("output = %q, want trim run", got)
|
||||
}
|
||||
if !strings.Contains(got, "totals: run=6 skip=2") {
|
||||
t.Fatalf("output = %q, want totals run=6 skip=2", got)
|
||||
|
||||
@@ -4,7 +4,7 @@ import "testing"
|
||||
|
||||
func TestBuildFullPlanOrder(t *testing.T) {
|
||||
got := BuildFullPlan()
|
||||
want := []string{"prepare", "transcribe", "normalize", "merge", "polish", "analyze", "archive", "notify"}
|
||||
want := []string{"prepare", "transcribe", "merge", "polish", "trim", "analyze", "archive", "notify"}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("len(plan) = %d, want %d", len(got), len(want))
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ func TestResumeStartsAfterCompletedStages(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("load manifest: %v", err)
|
||||
}
|
||||
if loaded.Stages["normalize"] == nil || loaded.Stages["normalize"].Status != manifest.StatusSucceeded {
|
||||
t.Fatalf("normalize stage = %#v, want succeeded", loaded.Stages["normalize"])
|
||||
if loaded.Stages["trim"] == nil || loaded.Stages["trim"].Status != manifest.StatusSucceeded {
|
||||
t.Fatalf("trim stage = %#v, want succeeded", loaded.Stages["trim"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestResumeNoRemainingStages(t *testing.T) {
|
||||
|
||||
store := &manifest.LocalStore{}
|
||||
m := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
|
||||
for _, name := range []string{"prepare", "transcribe", "normalize", "merge", "polish", "analyze", "archive", "notify"} {
|
||||
for _, name := range []string{"prepare", "transcribe", "merge", "polish", "trim", "analyze", "archive", "notify"} {
|
||||
m.MarkStageSucceeded(name, time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
|
||||
}
|
||||
if err := store.Save(context.Background(), manifestPath, m); err != nil {
|
||||
@@ -85,7 +85,7 @@ func TestResumeForceRerunsSucceeded(t *testing.T) {
|
||||
|
||||
store := &manifest.LocalStore{}
|
||||
m := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
|
||||
for _, name := range []string{"prepare", "transcribe", "normalize", "merge", "polish", "analyze", "archive", "notify"} {
|
||||
for _, name := range []string{"prepare", "transcribe", "merge", "polish", "trim", "analyze", "archive", "notify"} {
|
||||
m.MarkStageSucceeded(name, time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
|
||||
}
|
||||
if err := store.Save(context.Background(), manifestPath, m); err != nil {
|
||||
@@ -165,3 +165,30 @@ func TestRunStageSkipAndForce(t *testing.T) {
|
||||
t.Fatalf("output = %q, want force rerun", out.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunStageTrimPlaceholderExecutes(t *testing.T) {
|
||||
workspaceRoot := t.TempDir()
|
||||
pipelinePath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
|
||||
manifestPath := filepath.Join(workspaceRoot, "work", "2026-05-03", "manifest.json")
|
||||
|
||||
var out bytes.Buffer
|
||||
err := RunStage(context.Background(), []string{"--config", pipelinePath, "--session", sessionPath, "trim"}, &out)
|
||||
if err != nil {
|
||||
t.Fatalf("RunStage(trim) error = %v", err)
|
||||
}
|
||||
if !strings.Contains(out.String(), "stage=trim executed=1 skipped=0") {
|
||||
t.Fatalf("output = %q, want stage=trim executed", out.String())
|
||||
}
|
||||
|
||||
store := &manifest.LocalStore{}
|
||||
m, err := store.Load(context.Background(), manifestPath)
|
||||
if err != nil {
|
||||
t.Fatalf("load manifest: %v", err)
|
||||
}
|
||||
if m.Stages["trim"] == nil || m.Stages["trim"].Status != manifest.StatusSucceeded {
|
||||
t.Fatalf("trim stage = %#v, want succeeded", m.Stages["trim"])
|
||||
}
|
||||
if m.Stages["trim"].Metadata == nil || m.Stages["trim"].Metadata["placeholder"] != true {
|
||||
t.Fatalf("trim stage metadata = %#v, want placeholder=true", m.Stages["trim"].Metadata)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func TestExecuteStagesPlaceholderSuccessUpdatesManifest(t *testing.T) {
|
||||
t.Fatalf("Load manifest error = %v", err)
|
||||
}
|
||||
|
||||
for _, name := range []string{"prepare", "transcribe", "normalize", "merge", "polish", "analyze", "archive", "notify"} {
|
||||
for _, name := range []string{"prepare", "transcribe", "merge", "polish", "trim", "analyze", "archive", "notify"} {
|
||||
sr := m.Stages[name]
|
||||
if sr == nil {
|
||||
t.Fatalf("missing stage record %q", name)
|
||||
@@ -238,8 +238,8 @@ func TestExecuteStagesFailureUpdatesManifest(t *testing.T) {
|
||||
if got := m.Stages["transcribe"]; got == nil || got.Status != manifest.StatusFailed {
|
||||
t.Fatalf("transcribe status = %#v, want failed", got)
|
||||
}
|
||||
if got := m.Stages["normalize"]; got != nil {
|
||||
t.Fatalf("normalize should not run, got %#v", got)
|
||||
if got := m.Stages["merge"]; got != nil {
|
||||
t.Fatalf("merge should not run, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ func (s *LocalStore) EnsureLayout(sessionID string) (SessionPaths, error) {
|
||||
paths.AudioDir,
|
||||
paths.TranscriptsDir,
|
||||
paths.TranscriptsRawDir,
|
||||
paths.TranscriptsNormalizedDir,
|
||||
paths.TranscriptsTrimmedDir,
|
||||
paths.ArtifactsDir,
|
||||
paths.ConfigDir,
|
||||
paths.LogsDir,
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestEnsureLayoutCreatesExpectedDirectories(t *testing.T) {
|
||||
checkDirExists(t, paths.AudioDir)
|
||||
checkDirExists(t, paths.TranscriptsDir)
|
||||
checkDirExists(t, paths.TranscriptsRawDir)
|
||||
checkDirExists(t, paths.TranscriptsNormalizedDir)
|
||||
checkDirExists(t, paths.TranscriptsTrimmedDir)
|
||||
checkDirExists(t, paths.ArtifactsDir)
|
||||
checkDirExists(t, paths.ConfigDir)
|
||||
checkDirExists(t, paths.LogsDir)
|
||||
|
||||
@@ -4,18 +4,18 @@ import "path/filepath"
|
||||
|
||||
// SessionPaths contains canonical local paths for one session work directory.
|
||||
type SessionPaths struct {
|
||||
WorkspaceRoot string
|
||||
Root string
|
||||
InputsDir string
|
||||
AudioDir string
|
||||
TranscriptsDir string
|
||||
TranscriptsRawDir string
|
||||
TranscriptsNormalizedDir string
|
||||
ArtifactsDir string
|
||||
ConfigDir string
|
||||
LogsDir string
|
||||
ManifestPath string
|
||||
LockPath string
|
||||
WorkspaceRoot string
|
||||
Root string
|
||||
InputsDir string
|
||||
AudioDir string
|
||||
TranscriptsDir string
|
||||
TranscriptsRawDir string
|
||||
TranscriptsTrimmedDir string
|
||||
ArtifactsDir string
|
||||
ConfigDir string
|
||||
LogsDir string
|
||||
ManifestPath string
|
||||
LockPath string
|
||||
}
|
||||
|
||||
// SessionWorkDir returns the work directory for one session.
|
||||
@@ -27,17 +27,17 @@ func buildSessionPaths(workspaceRoot, sessionID string) SessionPaths {
|
||||
root := SessionWorkDir(workspaceRoot, sessionID)
|
||||
transcripts := filepath.Join(root, "transcripts")
|
||||
return SessionPaths{
|
||||
WorkspaceRoot: workspaceRoot,
|
||||
Root: root,
|
||||
InputsDir: filepath.Join(root, "inputs"),
|
||||
AudioDir: filepath.Join(root, "audio"),
|
||||
TranscriptsDir: transcripts,
|
||||
TranscriptsRawDir: filepath.Join(transcripts, "raw"),
|
||||
TranscriptsNormalizedDir: filepath.Join(transcripts, "normalized"),
|
||||
ArtifactsDir: filepath.Join(root, "artifacts"),
|
||||
ConfigDir: filepath.Join(root, "config"),
|
||||
LogsDir: filepath.Join(root, "logs"),
|
||||
ManifestPath: filepath.Join(root, "manifest.json"),
|
||||
LockPath: filepath.Join(root, ".lock"),
|
||||
WorkspaceRoot: workspaceRoot,
|
||||
Root: root,
|
||||
InputsDir: filepath.Join(root, "inputs"),
|
||||
AudioDir: filepath.Join(root, "audio"),
|
||||
TranscriptsDir: transcripts,
|
||||
TranscriptsRawDir: filepath.Join(transcripts, "raw"),
|
||||
TranscriptsTrimmedDir: filepath.Join(transcripts, "trimmed"),
|
||||
ArtifactsDir: filepath.Join(root, "artifacts"),
|
||||
ConfigDir: filepath.Join(root, "config"),
|
||||
LogsDir: filepath.Join(root, "logs"),
|
||||
ManifestPath: filepath.Join(root, "manifest.json"),
|
||||
LockPath: filepath.Join(root, ".lock"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,9 +90,9 @@ func All() []Stage {
|
||||
return []Stage{
|
||||
prepareStage{},
|
||||
transcribeStage{},
|
||||
placeholderStage{name: "normalize"},
|
||||
mergeStage{},
|
||||
polishStage{},
|
||||
placeholderStage{name: "trim"},
|
||||
analyzeStage{},
|
||||
placeholderStage{name: "archive"},
|
||||
placeholderStage{name: "notify"},
|
||||
|
||||
Reference in New Issue
Block a user