Compose resilient CLI processing and report lifecycle

This commit is contained in:
2026-09-24 17:48:52 +00:00
parent 87b8d9ad5c
commit 2f036b0356
11 changed files with 676 additions and 777 deletions

View File

@@ -151,7 +151,7 @@ func (s *OutputSession) Initialize(initial, failed ReportBundle) error {
// Finalize publishes the transcript first, then errors, recoveries, and Markdown.
// An empty transcript payload means processing failed and no transcript is written.
// Every enabled report is attempted even after an earlier write failure.
func (s *OutputSession) Finalize(transcript []byte, reports, failed ReportBundle) []FileFailure {
func (s *OutputSession) Finalize(transcript []byte, reports ReportBundle, failed func([]FileFailure) ReportBundle) []FileFailure {
if s == nil {
return []FileFailure{{Kind: "output finalization", Err: fmt.Errorf("output session is required")}}
}
@@ -168,7 +168,7 @@ func (s *OutputSession) Finalize(transcript []byte, reports, failed ReportBundle
}
}
if len(failures) > 0 {
for i, payload := range bundleParts(failed) {
for i, payload := range bundleParts(failed(failures)) {
_ = s.replace(s.reports[i], payload)
}
}

View File

@@ -57,7 +57,7 @@ func TestPrepareOutputPreservesExistingTranscriptAndChecksParent(t *testing.T) {
if got, err := os.ReadFile(output); err != nil || string(got) != "old content" {
t.Fatalf("preflight truncated output: %s %v", got, err)
}
if failures := session.Finalize([]byte("new content"), ReportBundle{}, ReportBundle{}); len(failures) != 0 {
if failures := session.Finalize([]byte("new content"), ReportBundle{}, func([]FileFailure) ReportBundle { return ReportBundle{} }); len(failures) != 0 {
t.Fatal(failures)
}
if got, err := os.ReadFile(output); err != nil || string(got) != "new content" {
@@ -127,7 +127,7 @@ func TestStdoutModeInitializesAndFinalizesReports(t *testing.T) {
t.Fatalf("initial %s: %s %v", path, got, err)
}
}
if failures := session.Finalize(nil, finished, ReportBundle{}); len(failures) != 0 {
if failures := session.Finalize(nil, finished, func([]FileFailure) ReportBundle { return ReportBundle{} }); len(failures) != 0 {
t.Fatal(failures)
}
for i, path := range session.ReportPaths() {
@@ -186,7 +186,7 @@ func TestFinalizationCollectsFailuresAndRetainsPublishedTranscript(t *testing.T)
}
complete := ReportBundle{Errors: []byte("complete"), Recoveries: []byte("complete"), Markdown: []byte("complete")}
failed := ReportBundle{Errors: []byte("failed"), Recoveries: []byte("failed"), Markdown: []byte("failed")}
failures := session.Finalize([]byte("transcript"), complete, failed)
failures := session.Finalize([]byte("transcript"), complete, func([]FileFailure) ReportBundle { return failed })
if len(failures) != 1 || failures[0].Path != filepath.Join(dir, ErrorsFile) {
t.Fatalf("failures: %+v", failures)
}