Archive Audita reports as polish diagnostics

This commit is contained in:
2026-09-25 15:51:18 +00:00
parent 77d9d57937
commit 569f467393
9 changed files with 212 additions and 14 deletions

View File

@@ -87,7 +87,8 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
res, err := env.Audita.Run(ctx, req)
if err != nil {
return nil, fmt.Errorf("polish: audita polish failed: %w", err)
return nil, fmt.Errorf("polish: audita polish failed (output_dir=%q, stdout_log=%q, stderr_log=%q): %w",
filepath.Dir(processedPath), stdoutPath, stderrPath, err)
}
finalProcessedPath, err := authoritativeOutputPath(processedPath, res.ProcessedTranscriptPath)
@@ -97,6 +98,27 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
if err := validateProcessedTranscriptOutput(finalProcessedPath); err != nil {
return nil, fmt.Errorf("polish: processed transcript %q invalid: %w", finalProcessedPath, err)
}
reportPaths := make([]string, 0, 3)
reportMetadata := map[string]string{}
for _, report := range []struct{ name, returned string }{
{"errors.yml", res.Reports.ErrorsPath},
{"recoveries.yml", res.Reports.RecoveriesPath},
{"report.md", res.Reports.OverviewPath},
} {
if report.returned == "" {
continue
}
expected := filepath.Join(filepath.Dir(processedPath), report.name)
path, err := authoritativeOutputPath(expected, report.returned)
if err != nil {
return nil, fmt.Errorf("polish: report %s: %w", report.name, err)
}
if err := requireRegularReadableFile(path, "audita report"); err != nil {
return nil, fmt.Errorf("polish: %w", err)
}
reportPaths = append(reportPaths, path)
reportMetadata[report.name] = path
}
materializedProcessed, err := materializeRunLocalOutput(env.ArtifactStore, finalProcessedPath, canonicalProcessedPath, artifacts.Ref{
Kind: "transcript_polished",
@@ -133,6 +155,7 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
"adapter_generated_config_path": res.GeneratedConfigPath,
"adapter_stdout_log_path": res.StdoutLogPath,
"adapter_stderr_log_path": res.StderrLogPath,
"report_paths": reportMetadata,
}
if res.Metadata != nil {
meta["adapter_metadata"] = res.Metadata
@@ -140,7 +163,7 @@ func (polishStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
return &StageResult{
Outputs: outputs,
Logs: []string{stdoutPath, stderrPath},
Logs: append([]string{stdoutPath, stderrPath}, reportPaths...),
GeneratedConfigs: []string{generatedConfigPath},
Metadata: meta,
}, nil

View File

@@ -3,7 +3,9 @@ package stage
import (
"context"
"errors"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"time"
@@ -15,6 +17,12 @@ import (
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
)
type polishRunnerFunc func(context.Context, audita.PolishRequest) (audita.PolishResult, error)
func (run polishRunnerFunc) Run(ctx context.Context, req audita.PolishRequest) (audita.PolishResult, error) {
return run(ctx, req)
}
func TestPolishStagePolishesMergedTranscriptAndRecordsMetadata(t *testing.T) {
env, m := setupPolishEnv(t)
paths := sessionPathsForEnv(env, m.SessionID)
@@ -50,8 +58,14 @@ func TestPolishStagePolishesMergedTranscriptAndRecordsMetadata(t *testing.T) {
if result.Outputs[0].Kind != "transcript_polished" {
t.Fatalf("output[0] kind = %q, want transcript_polished", result.Outputs[0].Kind)
}
if len(result.Logs) != 2 {
t.Fatalf("logs = %#v, want 2", result.Logs)
if len(result.Logs) != 5 {
t.Fatalf("logs = %#v, want stdout, stderr, and three reports", result.Logs)
}
for _, name := range []string{"errors.yml", "recoveries.yml", "report.md"} {
path := filepath.Join(filepath.Dir(req.OutputProcessedPath), name)
if !slices.Contains(result.Logs, path) {
t.Fatalf("report %q missing from diagnostics: %#v", path, result.Logs)
}
}
if len(result.GeneratedConfigs) != 1 {
t.Fatalf("generated configs = %#v, want 1", result.GeneratedConfigs)
@@ -63,6 +77,9 @@ func TestPolishStagePolishesMergedTranscriptAndRecordsMetadata(t *testing.T) {
if result.Metadata["output_schema"] != "audita-v1" {
t.Fatalf("metadata output_schema = %#v, want audita-v1", result.Metadata["output_schema"])
}
if _, err := os.Stat(filepath.Join(paths.ArtifactsDir, "audita.report.json")); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("legacy report destination exists: %v", err)
}
}
func TestPolishStageRejectsIncidentalMergedTranscriptPath(t *testing.T) {
@@ -149,6 +166,58 @@ func TestPolishStageFailsWhenAdapterFails(t *testing.T) {
}
}
func TestPolishStageDoesNotPromoteTranscriptAfterAdapterFailure(t *testing.T) {
env, m := setupPolishEnv(t)
env.Config.Session.Campaign = "sample-campaign"
m.Campaign = "sample-campaign"
m.RunID = "20260518T010203Z-abcdef12"
paths := sessionPathsForEnv(env, m.SessionID)
mergedPath := filepath.Join(paths.TranscriptsDir, "base.json")
writeFile(t, mergedPath, `{"segments":[]}`)
setCurrentBuiltInEvidence(t, m, "merge", artifacts.TranscriptOutputKindBase, artifacts.ArtifactTranscriptBase, mergedPath)
writeFile(t, filepath.Join(paths.InputsDir, "glossary.yml"), "terms: []\n")
failure := errors.New("audita exited after output")
var runOutput string
env.Audita = polishRunnerFunc(func(_ context.Context, req audita.PolishRequest) (audita.PolishResult, error) {
runOutput = req.OutputProcessedPath
writeFile(t, runOutput, `{"segments":[]}`)
writeFile(t, filepath.Join(filepath.Dir(runOutput), "errors.yml"), "status: failed\n")
return audita.PolishResult{ProcessedTranscriptPath: runOutput}, failure
})
_, err := (polishStage{}).Run(context.Background(), env, m)
if !errors.Is(err, failure) || !strings.Contains(err.Error(), filepath.Dir(runOutput)) {
t.Fatalf("error = %v, want original failure with output directory", err)
}
if _, err := os.Stat(filepath.Join(paths.TranscriptsDir, "polished.json")); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("failed output was promoted: %v", err)
}
if _, err := os.Stat(filepath.Join(filepath.Dir(runOutput), "errors.yml")); err != nil {
t.Fatalf("run-local diagnostic missing: %v", err)
}
}
func TestPolishStageRejectsRedirectedReportPath(t *testing.T) {
env, m := setupPolishEnv(t)
env.Config.Session.Campaign = "sample-campaign"
m.Campaign = "sample-campaign"
m.RunID = "20260518T010203Z-abcdef12"
paths := sessionPathsForEnv(env, m.SessionID)
mergedPath := filepath.Join(paths.TranscriptsDir, "base.json")
writeFile(t, mergedPath, `{"segments":[]}`)
setCurrentBuiltInEvidence(t, m, "merge", artifacts.TranscriptOutputKindBase, artifacts.ArtifactTranscriptBase, mergedPath)
writeFile(t, filepath.Join(paths.InputsDir, "glossary.yml"), "terms: []\n")
redirected := filepath.Join(t.TempDir(), "errors.yml")
writeFile(t, redirected, "status: completed\n")
env.Audita = &audita.FakeRunner{Result: audita.PolishResult{Reports: audita.ReportBundle{ErrorsPath: redirected}}}
_, err := (polishStage{}).Run(context.Background(), env, m)
if err == nil || !strings.Contains(err.Error(), "did not match requested path") {
t.Fatalf("error = %v, want authoritative report path rejection", err)
}
if _, err := os.Stat(filepath.Join(paths.TranscriptsDir, "polished.json")); !errors.Is(err, os.ErrNotExist) {
t.Fatalf("report-path failure promoted output: %v", err)
}
}
func TestPolishStageFailsWhenProcessedOutputInvalid(t *testing.T) {
env, m := setupPolishEnv(t)
paths := sessionPathsForEnv(env, m.SessionID)

View File

@@ -1131,6 +1131,32 @@ func TestPublishFailsWithoutObjectStore(t *testing.T) {
}
}
func TestPublishArchivesCurrentPolishReportsOnlyAsDiagnostics(t *testing.T) {
env, m, runRoot := publishFixture(t)
polish := m.Stages["polish"]
polish.Outputs = nil
for _, name := range []string{"errors.yml", "recoveries.yml", "report.md"} {
path := filepath.Join(runRoot, "polish", "outputs", "transcripts", name)
writeStageTestFile(t, path, "diagnostic\n")
polish.Logs = append(polish.Logs, path)
}
if _, err := (publishStage{}).Run(context.Background(), env, m); err != nil {
t.Fatalf("publish reports: %v", err)
}
fake := env.ObjectStore.(*storage.FakeBackend)
for _, name := range []string{"errors.yml", "recoveries.yml", "report.md"} {
relative := "polish/outputs/transcripts/" + name
if _, ok := fake.Objects[m.S3RunPrefix+relative]; !ok {
t.Fatalf("missing run-archive diagnostic %q", relative)
}
for key := range fake.Objects {
if strings.Contains(key, "published/"+name) {
t.Fatalf("report was published as canonical output: %q", key)
}
}
}
}
func publishFixture(t *testing.T) (*Env, *manifest.Manifest, string) {
t.Helper()