Add diagnostics references to reports
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -296,7 +297,7 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
|||||||
|
|
||||||
if runErr != nil {
|
if runErr != nil {
|
||||||
errorPhase, errorMessage := extractErrorPhase(runErr)
|
errorPhase, errorMessage := extractErrorPhase(runErr)
|
||||||
report := buildProcessReport("failed", inv, startedAt, completedAt, errorMessage, errorPhase, nil, nil)
|
report := buildProcessReport("failed", inv, runDir, startedAt, completedAt, errorMessage, errorPhase, nil, nil)
|
||||||
|
|
||||||
if strings.TrimSpace(inv.ReportJSONPath) != "" {
|
if strings.TrimSpace(inv.ReportJSONPath) != "" {
|
||||||
if err := reporting.WriteProcessReport(inv.ReportJSONPath, report); err != nil {
|
if err := reporting.WriteProcessReport(inv.ReportJSONPath, report); err != nil {
|
||||||
@@ -313,7 +314,7 @@ func runProcess(args []string, stdout, stderr io.Writer) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
report := buildProcessReport("success", inv, startedAt, completedAt, "", "", normSummary, chunkSummary)
|
report := buildProcessReport("success", inv, runDir, startedAt, completedAt, "", "", normSummary, chunkSummary)
|
||||||
|
|
||||||
if strings.TrimSpace(inv.ReportJSONPath) != "" {
|
if strings.TrimSpace(inv.ReportJSONPath) != "" {
|
||||||
if err := reporting.WriteProcessReport(inv.ReportJSONPath, report); err != nil {
|
if err := reporting.WriteProcessReport(inv.ReportJSONPath, report); err != nil {
|
||||||
@@ -348,7 +349,7 @@ func extractErrorPhase(err error) (phase string, message string) {
|
|||||||
return "", msg
|
return "", msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildProcessReport(status string, inv processInvocation, startedAt, completedAt time.Time, errorMessage string, errorPhase string, normalizationSummary *normalization.NormalizationSummary, chunkingSummary *chunking.Summary) reporting.ProcessReport {
|
func buildProcessReport(status string, inv processInvocation, runDir *diagnostics.RunDirectory, startedAt, completedAt time.Time, errorMessage string, errorPhase string, normalizationSummary *normalization.NormalizationSummary, chunkingSummary *chunking.Summary) reporting.ProcessReport {
|
||||||
report := reporting.ProcessReport{
|
report := reporting.ProcessReport{
|
||||||
Phase: "phase3-chunking",
|
Phase: "phase3-chunking",
|
||||||
Status: status,
|
Status: status,
|
||||||
@@ -361,6 +362,22 @@ func buildProcessReport(status string, inv processInvocation, startedAt, complet
|
|||||||
CompletedAt: &completedAt,
|
CompletedAt: &completedAt,
|
||||||
ErrorPhase: errorPhase,
|
ErrorPhase: errorPhase,
|
||||||
}
|
}
|
||||||
|
if runDir != nil {
|
||||||
|
diagnosticsDir := runDir.Path()
|
||||||
|
report.Diagnostics = &reporting.DiagnosticsMetadata{
|
||||||
|
DirectoryPath: diagnosticsDir,
|
||||||
|
SourceTranscriptPath: filepath.Join(diagnosticsDir, "source-transcript.json"),
|
||||||
|
ParsedSourceTranscriptPath: filepath.Join(diagnosticsDir, "source-transcript-parsed.json"),
|
||||||
|
NormalizedTranscriptPath: filepath.Join(diagnosticsDir, "normalized-transcript.json"),
|
||||||
|
NormalizationSummaryPath: filepath.Join(diagnosticsDir, "normalization-summary.json"),
|
||||||
|
ChunkingSummaryPath: filepath.Join(diagnosticsDir, "chunking-summary.json"),
|
||||||
|
InvocationMetadataPath: filepath.Join(diagnosticsDir, "invocation.json"),
|
||||||
|
RedactedEffectiveConfigPath: filepath.Join(diagnosticsDir, "effective-config.json"),
|
||||||
|
}
|
||||||
|
if status == "failed" {
|
||||||
|
report.Diagnostics.ErrorLogPath = filepath.Join(diagnosticsDir, "error.log")
|
||||||
|
}
|
||||||
|
}
|
||||||
if errorMessage != "" {
|
if errorMessage != "" {
|
||||||
report.ErrorMessage = errorMessage
|
report.ErrorMessage = errorMessage
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -276,6 +276,21 @@ func TestRunProcessReportJSONSuccessIncludesNormalizationSummary(t *testing.T) {
|
|||||||
if report.NormalizationMerges == nil || *report.NormalizationMerges != 1 {
|
if report.NormalizationMerges == nil || *report.NormalizationMerges != 1 {
|
||||||
t.Fatalf("expected 1 merge, got %v", report.NormalizationMerges)
|
t.Fatalf("expected 1 merge, got %v", report.NormalizationMerges)
|
||||||
}
|
}
|
||||||
|
if report.Diagnostics == nil {
|
||||||
|
t.Fatalf("expected diagnostics metadata in success report")
|
||||||
|
}
|
||||||
|
if report.Diagnostics.DirectoryPath == "" {
|
||||||
|
t.Fatalf("expected diagnostics directory path in success report")
|
||||||
|
}
|
||||||
|
if report.Diagnostics.SourceTranscriptPath == "" ||
|
||||||
|
report.Diagnostics.ParsedSourceTranscriptPath == "" ||
|
||||||
|
report.Diagnostics.NormalizedTranscriptPath == "" ||
|
||||||
|
report.Diagnostics.NormalizationSummaryPath == "" ||
|
||||||
|
report.Diagnostics.ChunkingSummaryPath == "" ||
|
||||||
|
report.Diagnostics.InvocationMetadataPath == "" ||
|
||||||
|
report.Diagnostics.RedactedEffectiveConfigPath == "" {
|
||||||
|
t.Fatalf("expected populated diagnostics artifact references in success report: %+v", report.Diagnostics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunProcessReportJSONBestEffortOnFailure(t *testing.T) {
|
func TestRunProcessReportJSONBestEffortOnFailure(t *testing.T) {
|
||||||
@@ -283,6 +298,7 @@ func TestRunProcessReportJSONBestEffortOnFailure(t *testing.T) {
|
|||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
reportPath := filepath.Join(t.TempDir(), "report.json")
|
reportPath := filepath.Join(t.TempDir(), "report.json")
|
||||||
|
workDir := t.TempDir()
|
||||||
exitCode := Run([]string{
|
exitCode := Run([]string{
|
||||||
"process",
|
"process",
|
||||||
fixturePath("malformed_transcript.json"),
|
fixturePath("malformed_transcript.json"),
|
||||||
@@ -290,6 +306,10 @@ func TestRunProcessReportJSONBestEffortOnFailure(t *testing.T) {
|
|||||||
fixturePath("tiny_glossary.yaml"),
|
fixturePath("tiny_glossary.yaml"),
|
||||||
"--report-json",
|
"--report-json",
|
||||||
reportPath,
|
reportPath,
|
||||||
|
"--work-dir",
|
||||||
|
workDir,
|
||||||
|
"--work-dir-retention",
|
||||||
|
"always",
|
||||||
}, &stdout, &stderr)
|
}, &stdout, &stderr)
|
||||||
if exitCode == 0 {
|
if exitCode == 0 {
|
||||||
t.Fatalf("expected nonzero exit code")
|
t.Fatalf("expected nonzero exit code")
|
||||||
@@ -308,6 +328,15 @@ func TestRunProcessReportJSONBestEffortOnFailure(t *testing.T) {
|
|||||||
if !strings.Contains(report.ErrorMessage, "not valid JSON") {
|
if !strings.Contains(report.ErrorMessage, "not valid JSON") {
|
||||||
t.Fatalf("expected parse error message, got %q", report.ErrorMessage)
|
t.Fatalf("expected parse error message, got %q", report.ErrorMessage)
|
||||||
}
|
}
|
||||||
|
if report.Diagnostics == nil {
|
||||||
|
t.Fatalf("expected diagnostics metadata in failed report")
|
||||||
|
}
|
||||||
|
if report.Diagnostics.DirectoryPath == "" || report.Diagnostics.ErrorLogPath == "" {
|
||||||
|
t.Fatalf("expected diagnostics directory and error log references in failed report: %+v", report.Diagnostics)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(report.Diagnostics.ErrorLogPath); err != nil {
|
||||||
|
t.Fatalf("expected error log file at reported path: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunProcessReportJSONDoesNotLeakAPIKeys(t *testing.T) {
|
func TestRunProcessReportJSONDoesNotLeakAPIKeys(t *testing.T) {
|
||||||
@@ -338,6 +367,42 @@ func TestRunProcessReportJSONDoesNotLeakAPIKeys(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunProcessReportJSONAndRunDirReportShareDiagnosticsMetadata(t *testing.T) {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
|
workDir := t.TempDir()
|
||||||
|
reportPath := filepath.Join(t.TempDir(), "report.json")
|
||||||
|
|
||||||
|
exitCode := Run([]string{
|
||||||
|
"process",
|
||||||
|
fixturePath("tiny_transcript.json"),
|
||||||
|
"--glossary",
|
||||||
|
fixturePath("tiny_glossary.yaml"),
|
||||||
|
"--report-json",
|
||||||
|
reportPath,
|
||||||
|
"--work-dir",
|
||||||
|
workDir,
|
||||||
|
"--work-dir-retention",
|
||||||
|
"always",
|
||||||
|
}, &stdout, &stderr)
|
||||||
|
if exitCode != 0 {
|
||||||
|
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
externalReport := readProcessReport(t, reportPath)
|
||||||
|
runPath := onlyRunDir(t, workDir)
|
||||||
|
runDirReport := readProcessReport(t, filepath.Join(runPath, "report.json"))
|
||||||
|
|
||||||
|
if externalReport.Diagnostics == nil || runDirReport.Diagnostics == nil {
|
||||||
|
t.Fatalf("expected diagnostics metadata in both reports")
|
||||||
|
}
|
||||||
|
if *externalReport.Diagnostics != *runDirReport.Diagnostics {
|
||||||
|
t.Fatalf("expected same diagnostics metadata in --report-json and run-dir report\nexternal=%+v\nrun-dir=%+v",
|
||||||
|
*externalReport.Diagnostics, *runDirReport.Diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunProcessWritesNormalizationDiagnosticsArtifacts(t *testing.T) {
|
func TestRunProcessWritesNormalizationDiagnosticsArtifacts(t *testing.T) {
|
||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
|
|||||||
@@ -25,6 +25,19 @@ type ProcessReport struct {
|
|||||||
NormalizationIDReassignments *int `json:"normalization_id_reassignments,omitempty"`
|
NormalizationIDReassignments *int `json:"normalization_id_reassignments,omitempty"`
|
||||||
NormalizationSkipped NormalizationSkipped `json:"normalization_skipped,omitempty"`
|
NormalizationSkipped NormalizationSkipped `json:"normalization_skipped,omitempty"`
|
||||||
Chunking *ChunkingSummary `json:"chunking,omitempty"`
|
Chunking *ChunkingSummary `json:"chunking,omitempty"`
|
||||||
|
Diagnostics *DiagnosticsMetadata `json:"diagnostics,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiagnosticsMetadata struct {
|
||||||
|
DirectoryPath string `json:"directory_path,omitempty"`
|
||||||
|
SourceTranscriptPath string `json:"source_transcript_path,omitempty"`
|
||||||
|
ParsedSourceTranscriptPath string `json:"parsed_source_transcript_path,omitempty"`
|
||||||
|
NormalizedTranscriptPath string `json:"normalized_transcript_path,omitempty"`
|
||||||
|
NormalizationSummaryPath string `json:"normalization_summary_path,omitempty"`
|
||||||
|
ChunkingSummaryPath string `json:"chunking_summary_path,omitempty"`
|
||||||
|
InvocationMetadataPath string `json:"invocation_metadata_path,omitempty"`
|
||||||
|
RedactedEffectiveConfigPath string `json:"redacted_effective_config_path,omitempty"`
|
||||||
|
ErrorLogPath string `json:"error_log_path,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NormalizationSkipped struct {
|
type NormalizationSkipped struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user