Make configured artifacts manifest authoritative
This commit is contained in:
37
internal/app/analyze_evidence_test_helpers_test.go
Normal file
37
internal/app/analyze_evidence_test_helpers_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/artifactmodel"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
||||
)
|
||||
|
||||
func setAppAnalyzeEvidence(m *manifest.Manifest, key, relativePath string, body []byte) {
|
||||
now := time.Date(2026, 5, 19, 23, 0, 0, 0, time.UTC)
|
||||
record := m.Stages["analyze"]
|
||||
if record == nil {
|
||||
record = &manifest.StageRecord{Name: "analyze", Status: manifest.StatusSucceeded, CreatedAt: now, UpdatedAt: now}
|
||||
m.Stages["analyze"] = record
|
||||
}
|
||||
if record.AnalyzeArtifacts == nil {
|
||||
record.AnalyzeArtifacts = map[string]manifest.AnalyzeArtifactRecord{}
|
||||
}
|
||||
digest := sha256.Sum256(body)
|
||||
record.AnalyzeStateVersion = manifest.AnalyzeStateContractVersion
|
||||
record.AnalyzeArtifacts[key] = manifest.AnalyzeArtifactRecord{
|
||||
Key: key, Status: manifest.AnalyzeArtifactCurrent,
|
||||
FingerprintVersion: manifest.AnalyzeFingerprintContractVersion,
|
||||
Fingerprint: strings.Repeat("1", 64),
|
||||
Output: &manifest.ArtifactRecord{
|
||||
Kind: "scriptorium_artifact", SourceID: artifacts.ConfiguredArtifactSourceID(key), LocalPath: relativePath,
|
||||
Contract: &artifactmodel.ContractMetadata{MediaType: "text/markdown", SchemaID: "narratio." + key, SchemaVersion: "1"},
|
||||
ProducerRunID: "run-1", Checksum: hex.EncodeToString(digest[:]),
|
||||
},
|
||||
OutputSize: int64(len(body)), ProducerRunID: "run-1", UpdatedAt: now,
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ func buildHelperArtifactCatalog(cfg *config.Config, m *manifest.Manifest) (*arti
|
||||
if cfg.Pipeline.Notarius != nil && cfg.Pipeline.Notarius.Enabled {
|
||||
catalog.HydrateExtractionArtifacts(paths, m, extractionDefinitions)
|
||||
}
|
||||
catalog.HydrateAnalyzeArtifacts(paths, m, configured)
|
||||
return catalog, nil
|
||||
}
|
||||
|
||||
@@ -43,7 +44,11 @@ func writeArtifactList(out io.Writer, cfg *config.Config, catalog *artifacts.Art
|
||||
writeArtifactLine(out, artifacts.ArtifactBoundsSession, lockSet)
|
||||
fmt.Fprintln(out, "Configured:")
|
||||
for _, entry := range catalog.ListConfigured() {
|
||||
writeArtifactLine(out, entry.SourceID, lockSet)
|
||||
state := "unavailable"
|
||||
if entry.Available {
|
||||
state = "available"
|
||||
}
|
||||
writeExtractionArtifactLine(out, entry.SourceID, state, entry.Provenance, lockSet)
|
||||
}
|
||||
fmt.Fprintln(out, "Extraction:")
|
||||
for _, entry := range catalog.ListExtraction() {
|
||||
|
||||
54
internal/app/operator_artifact_rendering_test.go
Normal file
54
internal/app/operator_artifact_rendering_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
||||
)
|
||||
|
||||
func TestBuildHelperArtifactCatalogUsesAnalyzeManifestEvidence(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
cfg := &config.Config{
|
||||
Pipeline: &config.PipelineConfig{
|
||||
Workspace: config.WorkspaceConfig{Root: root},
|
||||
Scriptorium: &config.ScriptoriumConfig{Artifacts: map[string]config.ScriptoriumArtifactConfig{
|
||||
"session_recap": {Enabled: true, OutputPath: "artifacts/session_recap.md"},
|
||||
}},
|
||||
},
|
||||
Session: &config.SessionConfig{Campaign: "campaign", SessionID: "session"},
|
||||
}
|
||||
paths := artifacts.NewLocalStore(root).SessionPathsFor("campaign", "session")
|
||||
outputPath := filepath.Join(paths.ArtifactsDir, "session_recap.md")
|
||||
if err := os.MkdirAll(filepath.Dir(outputPath), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body := []byte("# recap\n")
|
||||
if err := os.WriteFile(outputPath, body, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m := manifest.New("session", time.Date(2026, 8, 29, 12, 0, 0, 0, time.UTC))
|
||||
|
||||
incidental, err := buildHelperArtifactCatalog(cfg, m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
entry, _ := incidental.Lookup(artifacts.ConfiguredArtifactSourceID("session_recap"))
|
||||
if entry.Available {
|
||||
t.Fatal("operator catalog advertised incidental configured artifact")
|
||||
}
|
||||
|
||||
setAppAnalyzeEvidence(m, "session_recap", "artifacts/session_recap.md", body)
|
||||
current, err := buildHelperArtifactCatalog(cfg, m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
entry, _ = current.Lookup(artifacts.ConfiguredArtifactSourceID("session_recap"))
|
||||
if !entry.Available || entry.Provenance != artifacts.ArtifactProvenanceCurrentAnalyzeManifest {
|
||||
t.Fatalf("operator catalog entry = %#v", entry)
|
||||
}
|
||||
}
|
||||
@@ -542,6 +542,7 @@ func publishStageCleanupFixture(t *testing.T) (*config.Config, cleanupSeed, stri
|
||||
for _, name := range []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim", "extract", "render", "analyze"} {
|
||||
seedManifest.MarkStageSucceeded(name, time.Now().UTC(), nil)
|
||||
}
|
||||
setAppAnalyzeEvidence(seedManifest, "session_recap", "artifacts/session_recap.md", []byte("# recap\n"))
|
||||
seedManifest.S3SessionPrefix = artifacts.S3SessionPrefix("dnd", cfg.Session.Campaign, cfg.Session.SessionID)
|
||||
seedManifest.S3RunPrefix = artifacts.S3RunPrefix(seedManifest.S3SessionPrefix, runID)
|
||||
if err := store.Save(context.Background(), manifestPathFor(cfg), seedManifest); err != nil {
|
||||
|
||||
@@ -282,6 +282,7 @@ func restoreWorkflowManifestJSON(t *testing.T, sessionID, campaign string) []byt
|
||||
for i, stageName := range stages {
|
||||
m.MarkStageSucceeded(stageName, now.Add(time.Duration(i+1)*time.Minute), nil)
|
||||
}
|
||||
setAppAnalyzeEvidence(m, "session_recap", "artifacts/session_recap.md", []byte("# restored recap\n"))
|
||||
path := filepath.Join(t.TempDir(), "manifest.json")
|
||||
if err := store.Save(context.Background(), path, m); err != nil {
|
||||
t.Fatalf("save workflow manifest fixture: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user