Review Scriptorium integration architecture
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run executes the placeholder stage pipeline and persists manifest state.
|
// Run executes the pipeline plan and persists manifest state.
|
||||||
func Run(ctx context.Context, args []string, out io.Writer) error {
|
func Run(ctx context.Context, args []string, out io.Writer) error {
|
||||||
fs := flag.NewFlagSet("run", flag.ContinueOnError)
|
fs := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||||
fs.SetOutput(io.Discard)
|
fs.SetOutput(io.Discard)
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ func (analyzeStage) Declares() IODecl {
|
|||||||
return IODecl{
|
return IODecl{
|
||||||
Inputs: []artifacts.Ref{
|
Inputs: []artifacts.Ref{
|
||||||
{Kind: "transcript_processed", Category: "transcripts", RelativePath: "transcripts/processed.json"},
|
{Kind: "transcript_processed", Category: "transcripts", RelativePath: "transcripts/processed.json"},
|
||||||
{Kind: "artifact", Category: "artifacts", RelativePath: "artifacts/session_recap.md"},
|
|
||||||
},
|
},
|
||||||
Outputs: []artifacts.Ref{
|
Outputs: []artifacts.Ref{
|
||||||
{Kind: "session_recap", Category: "artifacts", RelativePath: "artifacts/session_recap.md"},
|
{Kind: "session_recap", Category: "artifacts", RelativePath: "artifacts/session_recap.md"},
|
||||||
@@ -177,7 +176,7 @@ func (analyzeStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
|||||||
return nil, fmt.Errorf("analyze: scriptorium render returned validation_failed=true")
|
return nil, fmt.Errorf("analyze: scriptorium render returned validation_failed=true")
|
||||||
}
|
}
|
||||||
finalRenderOutputPath := coalesceString(renderRes.OutputPath, renderReq.OutputPath)
|
finalRenderOutputPath := coalesceString(renderRes.OutputPath, renderReq.OutputPath)
|
||||||
if err := requireNonEmptyFile(finalRenderOutputPath, "session recap render output"); err != nil {
|
if err := requireNonEmptyFile(finalRenderOutputPath, artifactName+" render output"); err != nil {
|
||||||
return nil, fmt.Errorf("analyze: %w", err)
|
return nil, fmt.Errorf("analyze: %w", err)
|
||||||
}
|
}
|
||||||
if err := validateJSONFile(finalRenderOutputPath); err != nil {
|
if err := validateJSONFile(finalRenderOutputPath); err != nil {
|
||||||
@@ -238,7 +237,7 @@ func (analyzeStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
|||||||
}
|
}
|
||||||
|
|
||||||
finalOutputPath := coalesceString(res.OutputPath, req.OutputPath)
|
finalOutputPath := coalesceString(res.OutputPath, req.OutputPath)
|
||||||
if err := requireNonEmptyFile(finalOutputPath, "session recap output"); err != nil {
|
if err := requireNonEmptyFile(finalOutputPath, artifactName+" output"); err != nil {
|
||||||
return nil, fmt.Errorf("analyze: %w", err)
|
return nil, fmt.Errorf("analyze: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,7 +381,7 @@ func resolveInputPathForRead(paths artifacts.SessionPaths, sessionDir, pathValue
|
|||||||
func resolveScriptoriumOutputPath(paths artifacts.SessionPaths, configured string) (string, error) {
|
func resolveScriptoriumOutputPath(paths artifacts.SessionPaths, configured string) (string, error) {
|
||||||
outputPath := strings.TrimSpace(configured)
|
outputPath := strings.TrimSpace(configured)
|
||||||
if outputPath == "" {
|
if outputPath == "" {
|
||||||
outputPath = "artifacts/session_recap.md"
|
return "", fmt.Errorf("scriptorium artifact output path is required")
|
||||||
}
|
}
|
||||||
if filepath.IsAbs(outputPath) {
|
if filepath.IsAbs(outputPath) {
|
||||||
return filepath.Clean(outputPath), nil
|
return filepath.Clean(outputPath), nil
|
||||||
|
|||||||
@@ -84,6 +84,29 @@ func TestAnalyzeRenderDebugFalseDoesNotCallRenderArtifact(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAnalyzeRenderDebugArtifactOverrideFalseWinsOverGlobalTrue(t *testing.T) {
|
||||||
|
env, m, fake := setupAnalyzeEnv(t)
|
||||||
|
paths := env.ArtifactStore.SessionPaths(m.SessionID)
|
||||||
|
writeAnalyzeFile(t, filepath.Join(paths.TranscriptsDir, "processed.json"), `{"segments":[]}`)
|
||||||
|
|
||||||
|
env.Config.Pipeline.Scriptorium.RenderDebug = true
|
||||||
|
artifact := env.Config.Pipeline.Scriptorium.Artifacts["session_recap"]
|
||||||
|
disabled := false
|
||||||
|
artifact.RenderDebug = &disabled
|
||||||
|
env.Config.Pipeline.Scriptorium.Artifacts["session_recap"] = artifact
|
||||||
|
|
||||||
|
result, err := (analyzeStage{}).Run(context.Background(), env, m)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Run() error = %v", err)
|
||||||
|
}
|
||||||
|
if len(fake.RenderRequests) != 0 {
|
||||||
|
t.Fatalf("render requests = %d, want 0", len(fake.RenderRequests))
|
||||||
|
}
|
||||||
|
if result.Metadata["render_debug_enabled"] != false {
|
||||||
|
t.Fatalf("render_debug_enabled = %#v, want false", result.Metadata["render_debug_enabled"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAnalyzeRenderDebugTrueCallsRenderBeforeRun(t *testing.T) {
|
func TestAnalyzeRenderDebugTrueCallsRenderBeforeRun(t *testing.T) {
|
||||||
env, m, _ := setupAnalyzeEnv(t)
|
env, m, _ := setupAnalyzeEnv(t)
|
||||||
paths := env.ArtifactStore.SessionPaths(m.SessionID)
|
paths := env.ArtifactStore.SessionPaths(m.SessionID)
|
||||||
@@ -365,6 +388,27 @@ func TestAnalyzeFailsWhenRequiredPreviousRecapMissing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAnalyzeFailsWhenOutputPathMissing(t *testing.T) {
|
||||||
|
env, m, fake := setupAnalyzeEnv(t)
|
||||||
|
paths := env.ArtifactStore.SessionPaths(m.SessionID)
|
||||||
|
writeAnalyzeFile(t, filepath.Join(paths.TranscriptsDir, "processed.json"), `{"segments":[]}`)
|
||||||
|
|
||||||
|
artifact := env.Config.Pipeline.Scriptorium.Artifacts["session_recap"]
|
||||||
|
artifact.OutputPath = ""
|
||||||
|
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(), "output path") {
|
||||||
|
t.Fatalf("error = %q, want output path context", err.Error())
|
||||||
|
}
|
||||||
|
if len(fake.RunRequests) != 0 {
|
||||||
|
t.Fatalf("run requests = %d, want 0 when output path resolution fails", len(fake.RunRequests))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAnalyzeFailsWhenProcessedTranscriptMissing(t *testing.T) {
|
func TestAnalyzeFailsWhenProcessedTranscriptMissing(t *testing.T) {
|
||||||
env, m, _ := setupAnalyzeEnv(t)
|
env, m, _ := setupAnalyzeEnv(t)
|
||||||
_, err := (analyzeStage{}).Run(context.Background(), env, m)
|
_, err := (analyzeStage{}).Run(context.Background(), env, m)
|
||||||
|
|||||||
Reference in New Issue
Block a user