Update analyze-stage metadata and manifest output
This commit is contained in:
@@ -374,6 +374,7 @@ func executeAnalyzeArtifact(
|
||||
generatedConfigs := []string{}
|
||||
meta := map[string]any{
|
||||
"stage": "analyze",
|
||||
"name": artifactName,
|
||||
"artifact_name": artifactName,
|
||||
"source_id": artifacts.ConfiguredArtifactSourceID(artifactName),
|
||||
"output_kind": "scriptorium_artifact",
|
||||
@@ -507,6 +508,7 @@ func executeAnalyzeArtifact(
|
||||
logPaths = append(logPaths, stdoutLogPath, stderrLogPath)
|
||||
generatedConfigs = append(generatedConfigs, generatedConfigPath)
|
||||
meta["run_output_path"] = finalOutputPath
|
||||
meta["path"] = canonicalOutputPath
|
||||
meta["output_path"] = canonicalOutputPath
|
||||
meta["generated_config_path"] = generatedConfigPath
|
||||
meta["stdout_log_path"] = stdoutLogPath
|
||||
|
||||
@@ -444,6 +444,75 @@ func TestAnalyzeResolvesConfiguredArtifactInputFromDisabledArtifactOutput(t *tes
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeMetadataIncludesGeneratedAndReusedArtifacts(t *testing.T) {
|
||||
env, m, _ := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
writeAnalyzeFile(t, filepath.Join(paths.TranscriptsDir, "trimmed.json"), `{"segments":[]}`)
|
||||
playerHandoutPath := filepath.Join(paths.ArtifactsDir, "player_handout.md")
|
||||
writeAnalyzeFile(t, playerHandoutPath, "handout\n")
|
||||
|
||||
sessionRecap := env.Config.Pipeline.Scriptorium.Artifacts["session_recap"]
|
||||
sessionRecap.Inputs["recap"] = config.ScriptoriumInputConfig{
|
||||
Source: "narratio.artifact.player_handout",
|
||||
Required: true,
|
||||
}
|
||||
env.Config.Pipeline.Scriptorium.Artifacts["session_recap"] = sessionRecap
|
||||
env.Config.Pipeline.Scriptorium.Artifacts["player_handout"] = config.ScriptoriumArtifactConfig{
|
||||
Enabled: false,
|
||||
OutputPath: "artifacts/player_handout.md",
|
||||
}
|
||||
|
||||
result, err := (analyzeStage{}).Run(context.Background(), env, m)
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
|
||||
generated := mustArtifactEntryList(t, result.Metadata, "generated_artifacts")
|
||||
if len(generated) != 1 {
|
||||
t.Fatalf("generated_artifacts len = %d, want 1 (%#v)", len(generated), generated)
|
||||
}
|
||||
g0 := generated[0]
|
||||
if g0["name"] != "session_recap" {
|
||||
t.Fatalf("generated[0].name = %#v, want session_recap", g0["name"])
|
||||
}
|
||||
if g0["source_id"] != "narratio.artifact.session_recap" {
|
||||
t.Fatalf("generated[0].source_id = %#v, want narratio.artifact.session_recap", g0["source_id"])
|
||||
}
|
||||
if g0["output_kind"] != "scriptorium_artifact" {
|
||||
t.Fatalf("generated[0].output_kind = %#v, want scriptorium_artifact", g0["output_kind"])
|
||||
}
|
||||
if g0["path"] != filepath.Join(paths.ArtifactsDir, "session_recap.md") {
|
||||
t.Fatalf("generated[0].path = %#v, want session recap path", g0["path"])
|
||||
}
|
||||
if g0["prompt_id"] != "dnd.session_recap" {
|
||||
t.Fatalf("generated[0].prompt_id = %#v, want dnd.session_recap", g0["prompt_id"])
|
||||
}
|
||||
if g0["profile_id"] != "local-quality" {
|
||||
t.Fatalf("generated[0].profile_id = %#v, want local-quality", g0["profile_id"])
|
||||
}
|
||||
if g0["provenance"] != artifacts.ArtifactProvenanceGeneratedCurrentAnalyzeRun {
|
||||
t.Fatalf("generated[0].provenance = %#v, want %q", g0["provenance"], artifacts.ArtifactProvenanceGeneratedCurrentAnalyzeRun)
|
||||
}
|
||||
|
||||
reused := mustArtifactEntryList(t, result.Metadata, "reused_artifacts")
|
||||
if len(reused) != 1 {
|
||||
t.Fatalf("reused_artifacts len = %d, want 1 (%#v)", len(reused), reused)
|
||||
}
|
||||
r0 := reused[0]
|
||||
if r0["name"] != "player_handout" {
|
||||
t.Fatalf("reused[0].name = %#v, want player_handout", r0["name"])
|
||||
}
|
||||
if r0["source_id"] != "narratio.artifact.player_handout" {
|
||||
t.Fatalf("reused[0].source_id = %#v, want narratio.artifact.player_handout", r0["source_id"])
|
||||
}
|
||||
if r0["path"] != playerHandoutPath {
|
||||
t.Fatalf("reused[0].path = %#v, want %q", r0["path"], playerHandoutPath)
|
||||
}
|
||||
if r0["provenance"] != artifacts.ArtifactProvenanceDisabledFromDisk {
|
||||
t.Fatalf("reused[0].provenance = %#v, want %q", r0["provenance"], artifacts.ArtifactProvenanceDisabledFromDisk)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeRunsMultipleIndependentArtifactsInDeterministicOrder(t *testing.T) {
|
||||
env, m, fake := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
@@ -560,6 +629,41 @@ func TestAnalyzeAppliesSelectedArtifactsFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeMetadataIncludesMultipleGeneratedArtifacts(t *testing.T) {
|
||||
env, m, _ := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
writeAnalyzeFile(t, filepath.Join(paths.TranscriptsDir, "trimmed.json"), `{"segments":[]}`)
|
||||
|
||||
env.Config.Pipeline.Scriptorium.Artifacts["player_handout"] = config.ScriptoriumArtifactConfig{
|
||||
Enabled: true,
|
||||
PromptID: "dnd.player_handout",
|
||||
ProfileID: "local-quality",
|
||||
OutputPath: "artifacts/player_handout.md",
|
||||
Inputs: map[string]config.ScriptoriumInputConfig{
|
||||
"transcript": {
|
||||
Source: "narratio.transcript.trimmed",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := (analyzeStage{}).Run(context.Background(), env, m)
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
generated := mustArtifactEntryList(t, result.Metadata, "generated_artifacts")
|
||||
if len(generated) != 2 {
|
||||
t.Fatalf("generated_artifacts len = %d, want 2 (%#v)", len(generated), generated)
|
||||
}
|
||||
for i, entry := range generated {
|
||||
for _, field := range []string{"name", "source_id", "output_kind", "path", "prompt_id", "profile_id", "provenance"} {
|
||||
if _, ok := entry[field]; !ok {
|
||||
t.Fatalf("generated[%d] missing field %q: %#v", i, field, entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeFailsWhenRequiredConfiguredArtifactMissing(t *testing.T) {
|
||||
env, m, _ := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
@@ -914,6 +1018,22 @@ func TestAnalyzeSkipsWhenNoEnabledScriptoriumArtifactsConfigured(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeSkipsWhenArtifactMapEmpty(t *testing.T) {
|
||||
env, m, _ := setupAnalyzeEnv(t)
|
||||
env.Config.Pipeline.Scriptorium.Artifacts = map[string]config.ScriptoriumArtifactConfig{}
|
||||
|
||||
result, err := (analyzeStage{}).Run(context.Background(), env, m)
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
if result.Metadata["skipped"] != true {
|
||||
t.Fatalf("metadata = %#v, want skipped=true", result.Metadata)
|
||||
}
|
||||
if result.Metadata["reason"] != "no scriptorium artifacts configured" {
|
||||
t.Fatalf("reason = %#v, want no scriptorium artifacts configured", result.Metadata["reason"])
|
||||
}
|
||||
}
|
||||
|
||||
func setupAnalyzeEnv(t *testing.T) (*Env, *manifest.Manifest, *scriptorium.FakeRunner) {
|
||||
t.Helper()
|
||||
workspace := t.TempDir()
|
||||
@@ -1001,3 +1121,27 @@ func writeAnalyzeFileNoTest(path, contents string) {
|
||||
_ = os.MkdirAll(filepath.Dir(path), 0o755)
|
||||
_ = os.WriteFile(path, []byte(contents), 0o644)
|
||||
}
|
||||
|
||||
func mustArtifactEntryList(t *testing.T, metadata map[string]any, key string) []map[string]any {
|
||||
t.Helper()
|
||||
raw, ok := metadata[key]
|
||||
if !ok {
|
||||
t.Fatalf("metadata missing key %q: %#v", key, metadata)
|
||||
}
|
||||
if typed, ok := raw.([]map[string]any); ok {
|
||||
return typed
|
||||
}
|
||||
asList, ok := raw.([]any)
|
||||
if !ok {
|
||||
t.Fatalf("metadata[%q] = %#v, want []map[string]any", key, raw)
|
||||
}
|
||||
out := make([]map[string]any, 0, len(asList))
|
||||
for _, item := range asList {
|
||||
m, ok := item.(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("metadata[%q] entry = %#v, want map[string]any", key, item)
|
||||
}
|
||||
out = append(out, m)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user