Finish render rollout with markdown publish defaults, analyze guidance, and docs updates
This commit is contained in:
@@ -1002,6 +1002,8 @@ func TestExecutePublishLoadsRemoteLocks(t *testing.T) {
|
||||
_ = stageName
|
||||
}
|
||||
mustWriteTestFile(t, filepath.Join(workRoot, "transcripts", "final.trimmed.json"), `{"segments":[]}`)
|
||||
mustWriteTestFile(t, filepath.Join(workRoot, "transcripts", "final.md"), "# final\n")
|
||||
mustWriteTestFile(t, filepath.Join(workRoot, "transcripts", "final.trimmed.md"), "# final trimmed\n")
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
|
||||
@@ -273,7 +273,7 @@ func TestExecuteStagesPublishSkipsRequiredUnselectedConfiguredOutput(t *testing.
|
||||
manifestPath := manifestPathFor(cfg)
|
||||
seed := manifest.New(cfg.Session.SessionID, time.Now().UTC())
|
||||
seed.Campaign = cfg.Session.Campaign
|
||||
for _, stageName := range []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim"} {
|
||||
for _, stageName := range []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim", "render"} {
|
||||
seed.MarkStageSucceeded(stageName, time.Now().UTC(), nil)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(manifestPath), 0o755); err != nil {
|
||||
|
||||
@@ -67,6 +67,14 @@ func TestResolvePublishedDestination(t *testing.T) {
|
||||
t.Fatalf("built-in destination = %q, want transcripts/final.trimmed.json", got)
|
||||
}
|
||||
|
||||
got, err = ResolvePublishedDestination("narratio.transcript.final_markdown", "", configured)
|
||||
if err != nil {
|
||||
t.Fatalf("ResolvePublishedDestination(markdown built-in) error = %v", err)
|
||||
}
|
||||
if got != "transcripts/final.md" {
|
||||
t.Fatalf("markdown built-in destination = %q, want transcripts/final.md", got)
|
||||
}
|
||||
|
||||
got, err = ResolvePublishedDestination("narratio.artifact.session_recap", "", configured)
|
||||
if err != nil {
|
||||
t.Fatalf("ResolvePublishedDestination(configured) error = %v", err)
|
||||
@@ -103,6 +111,7 @@ func TestDescribeScriptoriumInputSource(t *testing.T) {
|
||||
wantErrLike string
|
||||
}{
|
||||
{name: "built in", source: "narratio.transcript.final_trimmed", wantKind: SourceKindBuiltIn},
|
||||
{name: "built in markdown", source: "narratio.transcript.final_markdown", wantKind: SourceKindBuiltIn},
|
||||
{name: "configured", source: "narratio.artifact.session_recap", wantKind: SourceKindConfiguredArtifact, wantKey: "session_recap"},
|
||||
{name: "previous", source: "narratio.previous_session.artifact.session_recap", wantKind: SourceKindPreviousArtifact, wantKey: "session_recap", wantPrev: true},
|
||||
{name: "invalid previous", source: "narratio.previous_session.artifact.", wantErr: ErrInvalidPreviousSessionSource},
|
||||
|
||||
@@ -86,6 +86,8 @@ const (
|
||||
// Callers should copy this slice before mutating.
|
||||
var DefaultPublishOutputs = []PublishOutputRule{
|
||||
{Source: artifactmodel.SourceTranscriptFinalTrimmed, Dest: PathTranscriptFinalTrimmed},
|
||||
{Source: artifactmodel.SourceTranscriptFinalMarkdown, Dest: artifactmodel.TranscriptPathFinalMarkdown},
|
||||
{Source: artifactmodel.SourceTranscriptFinalTrimmedMarkdown, Dest: artifactmodel.TranscriptPathFinalTrimmedMarkdown},
|
||||
}
|
||||
|
||||
// DefaultPipelineConfigSearchPaths defines the default search order for
|
||||
|
||||
@@ -200,6 +200,21 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
transcript:
|
||||
source: narratio.transcript.final_trimmed
|
||||
required: true
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "markdown built in artifact source is accepted",
|
||||
scriptoriumYAML: `scriptorium:
|
||||
binary: scriptorium
|
||||
artifacts:
|
||||
session_recap:
|
||||
enabled: true
|
||||
prompt_id: dnd.session_recap
|
||||
output_path: artifacts/session_recap.md
|
||||
inputs:
|
||||
transcript_markdown:
|
||||
source: narratio.transcript.final_markdown
|
||||
required: true
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -179,18 +179,29 @@ func TestSpoolAndPublishDefaults(t *testing.T) {
|
||||
if cfg.Pipeline.Publish.UploadRun == nil || !*cfg.Pipeline.Publish.UploadRun {
|
||||
t.Fatalf("publish.upload_run = %#v, want true", cfg.Pipeline.Publish.UploadRun)
|
||||
}
|
||||
if len(cfg.Pipeline.Publish.Outputs) != 1 {
|
||||
t.Fatalf("publish.outputs len = %d, want 1 default", len(cfg.Pipeline.Publish.Outputs))
|
||||
if len(cfg.Pipeline.Publish.Outputs) != 3 {
|
||||
t.Fatalf("publish.outputs len = %d, want 3 defaults", len(cfg.Pipeline.Publish.Outputs))
|
||||
}
|
||||
item := cfg.Pipeline.Publish.Outputs[0]
|
||||
if item.Required == nil || !*item.Required {
|
||||
t.Fatalf("publish.outputs[0].required = %#v, want true", item.Required)
|
||||
wantBySource := map[string]string{
|
||||
"narratio.transcript.final_trimmed": "transcripts/final.trimmed.json",
|
||||
"narratio.transcript.final_markdown": "transcripts/final.md",
|
||||
"narratio.transcript.final_trimmed_markdown": "transcripts/final.trimmed.md",
|
||||
}
|
||||
if item.Source != "narratio.transcript.final_trimmed" {
|
||||
t.Fatalf("publish.outputs[0].source = %q, want narratio.transcript.final_trimmed", item.Source)
|
||||
for i, item := range cfg.Pipeline.Publish.Outputs {
|
||||
if item.Required == nil || !*item.Required {
|
||||
t.Fatalf("publish.outputs[%d].required = %#v, want true", i, item.Required)
|
||||
}
|
||||
wantDest, ok := wantBySource[item.Source]
|
||||
if !ok {
|
||||
t.Fatalf("publish.outputs[%d].source = %q, want known default source", i, item.Source)
|
||||
}
|
||||
if item.Dest != wantDest {
|
||||
t.Fatalf("publish.outputs[%d].dest = %q, want %q", i, item.Dest, wantDest)
|
||||
}
|
||||
delete(wantBySource, item.Source)
|
||||
}
|
||||
if item.Dest != "transcripts/final.trimmed.json" {
|
||||
t.Fatalf("publish.outputs[0].dest = %q, want transcripts/final.trimmed.json", item.Dest)
|
||||
if len(wantBySource) != 0 {
|
||||
t.Fatalf("missing default publish outputs for sources: %#v", wantBySource)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +348,15 @@ publish:
|
||||
`,
|
||||
wantDest: "artifacts/session_recap.md",
|
||||
},
|
||||
{
|
||||
name: "markdown built in derives canonical destination",
|
||||
pipelineYML: testPipelineBaseYAML + `
|
||||
publish:
|
||||
outputs:
|
||||
- source: narratio.transcript.final_markdown
|
||||
`,
|
||||
wantDest: "transcripts/final.md",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -670,6 +670,12 @@ func resolveScriptoriumInput(
|
||||
return "", false, nil, fmt.Errorf("normalized transcript input is unavailable; run normalize stage first")
|
||||
case artifacts.ArtifactTranscriptFinalTrimmed:
|
||||
return "", false, nil, fmt.Errorf("trimmed transcript input is unavailable; run trim stage first")
|
||||
case artifacts.ArtifactTranscriptFinalMarkdown, artifacts.ArtifactTranscriptFinalTrimmedMarkdown:
|
||||
return "", false, nil, fmt.Errorf(
|
||||
"rendered markdown transcript input is unavailable for source %q; run narratio run-stage --force render %s",
|
||||
descriptor.Source.ID,
|
||||
paths.SessionID,
|
||||
)
|
||||
default:
|
||||
return "", false, nil, nil
|
||||
}
|
||||
|
||||
@@ -961,6 +961,31 @@ func TestAnalyzeSupportsNormalizedTranscriptSourceWhenConfigured(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeSupportsRenderedMarkdownTranscriptSourceWhenConfigured(t *testing.T) {
|
||||
env, m, fake := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
markdownPath := filepath.Join(paths.TranscriptsDir, "final.md")
|
||||
writeAnalyzeFile(t, markdownPath, "# Session Transcript\n")
|
||||
|
||||
artifact := env.Config.Pipeline.Scriptorium.Artifacts["session_recap"]
|
||||
artifact.Inputs["transcript"] = config.ScriptoriumInputConfig{
|
||||
Source: "narratio.transcript.final_markdown",
|
||||
Required: true,
|
||||
}
|
||||
env.Config.Pipeline.Scriptorium.Artifacts["session_recap"] = artifact
|
||||
|
||||
_, err := (analyzeStage{}).Run(context.Background(), env, m)
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
if len(fake.RunRequests) != 1 {
|
||||
t.Fatalf("run requests = %d, want 1", len(fake.RunRequests))
|
||||
}
|
||||
if fake.RunRequests[0].InputPaths["transcript"] != markdownPath {
|
||||
t.Fatalf("transcript input = %q, want markdown transcript path", fake.RunRequests[0].InputPaths["transcript"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeSupportsCanonicalNormalizedTranscriptSourceFromManifestOutput(t *testing.T) {
|
||||
env, m, fake := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
@@ -1045,6 +1070,42 @@ func TestAnalyzeFailsWhenNormalizedTranscriptMissing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeFailsWhenRenderedMarkdownTranscriptMissing(t *testing.T) {
|
||||
env, m, _ := setupAnalyzeEnv(t)
|
||||
artifact := env.Config.Pipeline.Scriptorium.Artifacts["session_recap"]
|
||||
artifact.Inputs["transcript"] = config.ScriptoriumInputConfig{
|
||||
Source: "narratio.transcript.final_markdown",
|
||||
Required: true,
|
||||
}
|
||||
env.Config.Pipeline.Scriptorium.Artifacts["session_recap"] = artifact
|
||||
|
||||
_, err := (analyzeStage{}).Run(context.Background(), env, m)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "run narratio run-stage --force render") {
|
||||
t.Fatalf("error = %q, want render guidance", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeFailsWhenRenderedTrimmedMarkdownTranscriptMissing(t *testing.T) {
|
||||
env, m, _ := setupAnalyzeEnv(t)
|
||||
artifact := env.Config.Pipeline.Scriptorium.Artifacts["session_recap"]
|
||||
artifact.Inputs["transcript"] = config.ScriptoriumInputConfig{
|
||||
Source: "narratio.transcript.final_trimmed_markdown",
|
||||
Required: true,
|
||||
}
|
||||
env.Config.Pipeline.Scriptorium.Artifacts["session_recap"] = artifact
|
||||
|
||||
_, err := (analyzeStage{}).Run(context.Background(), env, m)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "run narratio run-stage --force render") {
|
||||
t.Fatalf("error = %q, want render guidance", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeFailsWhenProcessedTranscriptInvalidJSON(t *testing.T) {
|
||||
env, m, _ := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
|
||||
@@ -32,6 +32,7 @@ var publishPrerequisiteStages = []string{
|
||||
"polish",
|
||||
"normalize",
|
||||
"trim",
|
||||
"render",
|
||||
"analyze",
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user