From 7fc89768efb3a4d684e5077e0e655726f9a6b7ff Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Mon, 26 Oct 2026 03:30:19 +0000 Subject: [PATCH] Migrate PromptKit inspection reports to the reporter --- docs/roadmap/implementation.md | 14 ++- integrations/ckprompt/command.go | 159 ++++++++++++++------------ integrations/ckprompt/command_test.go | 133 ++++++++++++++++----- 3 files changed, 204 insertions(+), 102 deletions(-) diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index a89b2a2..89bbdea 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,6 +1,6 @@ # Invocation reporting implementation plan -Status: Stages 1–13 complete. Stage 14 is next. +Status: Stages 1–14 complete. Stage 15 is next. ## Objective and authority @@ -535,7 +535,7 @@ adapter files; Stage 20 will run the assembled suite. ## Stage 14: Migrate PromptKit listing and inspection commands -Status: pending. +Status: complete. Primary file: [command.go](../../integrations/ckprompt/command.go) and its catalog, definition, and requirements report helpers/tests. @@ -557,6 +557,16 @@ Validation: migrate existing report/selection tests; add representative successful quiet/verbose/debug execution and native acquisition failure with an independent warning. Reuse core reporting coverage for universal mechanics. +PromptKit catalog listing, definition inspection, and requirements inspection +now submit provider messages through the reporter and render complete reports +inside managed output. Acquisition failures use native terminal context when +available and preserve the original cause. Tests cover independent warning and +terminal-context delivery, successful ordinary/quiet/verbose/debug execution, +quiet encoding failure, cancellation, and result delivery observations. The +ckprompt suite cannot run yet because `validation_command.go` and +`smoke_command.go` still use the legacy reporting API scheduled for Stage 15; +Stage 20 will validate the assembled module. + ## Stage 15: Migrate request validation and smoke commands Status: pending. diff --git a/integrations/ckprompt/command.go b/integrations/ckprompt/command.go index 5097cac..246367b 100644 --- a/integrations/ckprompt/command.go +++ b/integrations/ckprompt/command.go @@ -185,23 +185,23 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string) commandkit. Run: func(invocation commandkit.Invocation, input *catalogInput[I]) commandkit.Outcome { switch name { case "prompts": - return runCatalogList(invocation, suite, input, suite.ListPrompts, encodePromptCatalog) + return runCatalogList(invocation, suite, input, name, suite.ListPrompts, encodePromptCatalog) case "profiles": - return runCatalogList(invocation, suite, input, suite.ListProfiles, encodeProfileCatalog) + return runCatalogList(invocation, suite, input, name, suite.ListProfiles, encodeProfileCatalog) case "backends": - return runCatalogList(invocation, suite, input, suite.ListBackends, encodeProfileCatalog) + return runCatalogList(invocation, suite, input, name, suite.ListBackends, encodeProfileCatalog) case "namespaces": list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]string], error) { return suite.ListNamespaces(ctx, NamespaceRequest{Selection: request.Selection, Kinds: input.kinds}, value, state) } - return runCatalogList(invocation, suite, input, list, encodeProfileCatalog) + return runCatalogList(invocation, suite, input, name, list, encodeProfileCatalog) case "collections": list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]promptkit.CollectionInfo], error) { return suite.ListCollections(ctx, request.Selection, value, state) } - return runCatalogList(invocation, suite, input, list, encodeCollectionCatalog) + return runCatalogList(invocation, suite, input, name, list, encodeCollectionCatalog) default: - return definitionFailure(fmt.Errorf("unknown PromptKit catalog %q", name), nil) + return definitionFailure("Unable to list the selected PromptKit catalog.", commandkit.Explanation{}, fmt.Errorf("unknown PromptKit catalog %q", name)) } }, }) @@ -385,12 +385,13 @@ func runCatalogList[T any, I, S any]( invocation commandkit.Invocation, suite Suite[I, S], input *catalogInput[I], + catalog string, list func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[T], error), encode func(T) ([]byte, error), ) commandkit.Outcome { if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, nil) + return definitionFailure("PromptKit catalog listing was canceled.", commandkit.Explanation{}, err) } } var state S @@ -399,30 +400,35 @@ func runCatalogList[T any, I, S any]( } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, nil) + return definitionFailure("PromptKit catalog listing was canceled.", commandkit.Explanation{}, err) } } acquired, err := list(invocation.Context, CatalogRequest{Selection: input.selection, Filter: input.filter}, input.selected, state) + queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics) if err != nil { - return definitionFailure(err, acquired.Diagnostics) + return definitionFailure(fmt.Sprintf("Unable to list PromptKit %s.", catalog), acquired.Explanation, err) } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, acquired.Diagnostics) + return definitionFailure("PromptKit catalog listing was canceled.", acquired.Explanation, err) } } - output, err := encode(acquired.Value) - if err != nil { - return definitionFailure(err, acquired.Diagnostics) - } - return writePromptReport(invocation, output, acquired.Diagnostics) + _ = invocation.Reporter().RenderOutput(func(writer io.Writer) error { + output, err := encode(acquired.Value) + if err != nil { + return err + } + _, err = writer.Write(output) + return err + }) + return commandkit.Success() } func runDefinitionInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *definitionInput[I], prompt bool) commandkit.Outcome { if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, nil) + return definitionFailure("PromptKit inspection was canceled.", commandkit.Explanation{}, err) } } var state S @@ -431,41 +437,43 @@ func runDefinitionInspection[I, S any](invocation commandkit.Invocation, suite S } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, nil) + return definitionFailure("PromptKit inspection was canceled.", commandkit.Explanation{}, err) } } if prompt { request := PromptRequest{Selection: input.selection, ID: input.id} acquired, err := suite.InspectPrompt(invocation.Context, request, input.selected, state) + queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics) if err != nil { - return definitionFailure(err, acquired.Diagnostics) + return definitionFailure(fmt.Sprintf("Unable to inspect PromptKit prompt %q.", input.id), acquired.Explanation, err) } if acquired.Value == nil { - return definitionFailure(errors.New("PromptKit prompt inspection returned nil"), acquired.Diagnostics) + return definitionFailure("PromptKit prompt inspection returned no result.", acquired.Explanation, nil) } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, acquired.Diagnostics) + return definitionFailure(fmt.Sprintf("Inspection of PromptKit prompt %q was canceled.", input.id), acquired.Explanation, err) } } - return renderPromptReport(invocation, promptReportFromInspection(acquired.Value), input.format, acquired.Diagnostics) + return renderPromptReport(invocation, acquired.Value, input.format) } request := ProfileRequest{Selection: input.selection, ID: input.id} acquired, err := suite.InspectProfile(invocation.Context, request, input.selected, state) + queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics) if err != nil { - return definitionFailure(err, acquired.Diagnostics) + return definitionFailure(fmt.Sprintf("Unable to inspect PromptKit profile %q.", input.id), acquired.Explanation, err) } if acquired.Value == nil { - return definitionFailure(errors.New("PromptKit profile inspection returned nil"), acquired.Diagnostics) + return definitionFailure("PromptKit profile inspection returned no result.", acquired.Explanation, nil) } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, acquired.Diagnostics) + return definitionFailure(fmt.Sprintf("Inspection of PromptKit profile %q was canceled.", input.id), acquired.Explanation, err) } } - return renderProfileReport(invocation, profileReportFromInspection(acquired.Value), input.format, acquired.Diagnostics) + return renderProfileReport(invocation, acquired.Value, input.format) } type promptInputReport struct { @@ -624,7 +632,7 @@ func profileReportFromInspection(inspection *promptkit.ProfileInspection) profil func runRequirementsInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *requirementsInput[I]) commandkit.Outcome { if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, nil) + return definitionFailure("PromptKit requirements inspection was canceled.", commandkit.Explanation{}, err) } } var state S @@ -633,56 +641,56 @@ func runRequirementsInspection[I, S any](invocation commandkit.Invocation, suite } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, nil) + return definitionFailure("PromptKit requirements inspection was canceled.", commandkit.Explanation{}, err) } } request := RequirementsRequest{Selection: input.selection, Inspection: input.inspection} acquired, err := suite.InspectRequirements(invocation.Context, request, input.selected, state) + queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics) if err != nil { - return definitionFailure(err, acquired.Diagnostics) + return definitionFailure(fmt.Sprintf("Unable to inspect requirements for PromptKit prompt %q.", input.inspection.PromptID), acquired.Explanation, err) } if acquired.Value == nil { - return definitionFailure(errors.New("PromptKit requirements inspection returned nil"), acquired.Diagnostics) + return definitionFailure("PromptKit requirements inspection returned no result.", acquired.Explanation, nil) } if invocation.Context != nil { if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, acquired.Diagnostics) + return definitionFailure(fmt.Sprintf("Requirements inspection for PromptKit prompt %q was canceled.", input.inspection.PromptID), acquired.Explanation, err) } } - output, err := encodePromptYAMLBytes(requirementsReportFromInspection(acquired.Value)) - if err != nil { - return definitionFailure(err, acquired.Diagnostics) - } - return writePromptReport(invocation, output, acquired.Diagnostics) + _ = invocation.Reporter().RenderOutput(func(writer io.Writer) error { + return encodePromptYAML(writer, requirementsReportFromInspection(acquired.Value)) + }) + return commandkit.Success() } -func renderPromptReport(invocation commandkit.Invocation, report promptInspectionReport, format string, diagnostics []commandkit.Diagnostic) commandkit.Outcome { - var output bytes.Buffer - if format == "yaml" { - if err := encodePromptYAML(&output, report); err != nil { - return definitionFailure(err, diagnostics) +func renderPromptReport(invocation commandkit.Invocation, inspection *promptkit.PromptInspection, format string) commandkit.Outcome { + _ = invocation.Reporter().RenderOutput(func(writer io.Writer) error { + report := promptReportFromInspection(inspection) + if format == "yaml" { + return encodePromptYAML(writer, report) } - } else { - output.WriteString(formatPromptText(report)) - } - return writePromptReport(invocation, output.Bytes(), diagnostics) + _, err := io.WriteString(writer, formatPromptText(report)) + return err + }) + return commandkit.Success() } -func renderProfileReport(invocation commandkit.Invocation, report profileInspectionReport, format string, diagnostics []commandkit.Diagnostic) commandkit.Outcome { - var output bytes.Buffer - if format == "yaml" { - if err := encodePromptYAML(&output, report); err != nil { - return definitionFailure(err, diagnostics) +func renderProfileReport(invocation commandkit.Invocation, inspection *promptkit.ProfileInspection, format string) commandkit.Outcome { + _ = invocation.Reporter().RenderOutput(func(writer io.Writer) error { + report := profileReportFromInspection(inspection) + if format == "yaml" { + return encodePromptYAML(writer, report) } - } else { text, err := formatProfileText(report) if err != nil { - return definitionFailure(err, diagnostics) + return err } - output.WriteString(text) - } - return writePromptReport(invocation, output.Bytes(), diagnostics) + _, err = io.WriteString(writer, text) + return err + }) + return commandkit.Success() } func formatPromptText(report promptInspectionReport) string { @@ -771,30 +779,33 @@ func encodePromptYAMLBytes(value any) ([]byte, error) { return output.Bytes(), nil } -func writePromptReport(invocation commandkit.Invocation, output []byte, diagnostics []commandkit.Diagnostic) commandkit.Outcome { - if invocation.Context != nil { - if err := invocation.Context.Err(); err != nil { - return definitionFailure(err, diagnostics) +func queuePromptDiagnostics(reporter *commandkit.Reporter, diagnostics []commandkit.Diagnostic) { + deferred := reporter.AtEnd() + for _, diagnostic := range diagnostics { + if diagnostic.Write == nil { + continue + } + switch diagnostic.Severity { + case commandkit.SeverityInfo: + _ = deferred.Info("", diagnostic.Write) + case commandkit.SeverityDebug: + _ = deferred.Debug("", diagnostic.Write) + case commandkit.SeverityProgress: + _ = reporter.Progress("", diagnostic.Write) + default: + _ = deferred.Warn("", diagnostic.Write) } } - written, err := invocation.Streams.Out.Write(output) - if err == nil && written != len(output) { - err = io.ErrShortWrite - } - if err != nil { - outcome := definitionFailure(err, diagnostics) - outcome.ReportIncomplete = true - outcome.DeliveryError = err - return outcome - } - outcome := commandkit.Success() - outcome.Diagnostics = diagnostics - return outcome } -func definitionFailure(cause error, diagnostics []commandkit.Diagnostic) commandkit.Outcome { - outcome := commandkit.Failure(cause) - outcome.Diagnostics = diagnostics +func definitionFailure(message string, explanation commandkit.Explanation, cause error) commandkit.Outcome { + outcome := commandkit.Failure(message) + if explanation.Message != "" || len(explanation.Details) > 0 { + outcome = outcome.WithExplanation(explanation.Message, explanation.Details...) + } + if cause != nil { + outcome = outcome.WithCause(cause) + } return outcome } diff --git a/integrations/ckprompt/command_test.go b/integrations/ckprompt/command_test.go index 7df0290..4e3f479 100644 --- a/integrations/ckprompt/command_test.go +++ b/integrations/ckprompt/command_test.go @@ -701,41 +701,39 @@ func TestCatalogRejectsOperandsAndUnavailableSelectors(t *testing.T) { } } -func TestCatalogFailuresRetainDiagnosticsAndDeliveryState(t *testing.T) { - diagnostic := commandkit.Diagnostic{Severity: commandkit.SeverityWarning, Write: func(writer io.Writer) error { - _, err := io.WriteString(writer, "catalog warning\n") - return err - }} - hasCatalogWarning := func(diagnostics []commandkit.Diagnostic) bool { - for _, got := range diagnostics { - if got.Severity != diagnostic.Severity || got.Write == nil { - continue - } - var output bytes.Buffer - if got.Write(&output) == nil && output.String() == "catalog warning\n" { - return true - } - } - return false - } +func TestCatalogFailureReportsWarningAndNativeExplanationSeparately(t *testing.T) { + diagnostic := commandkit.MessageDiagnostic(commandkit.SeverityWarning, "catalog warning") providerErr := errors.New("catalog provider failed") app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { - return commandkit.Acquired[[]promptkit.PromptIdentity]{Diagnostics: []commandkit.Diagnostic{diagnostic}}, providerErr + return commandkit.Acquired[[]promptkit.PromptIdentity]{ + Diagnostics: []commandkit.Diagnostic{diagnostic}, + Explanation: commandkit.Explanation{Details: []commandkit.Detail{func(writer io.Writer) error { + _, err := io.WriteString(writer, "PromptKit lookup context\n") + return err + }}}, + }, providerErr }})) - outcome := app.Execute(context.Background(), []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: io.Discard, Err: io.Discard}) - if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, providerErr) || !hasCatalogWarning(outcome.Diagnostics) { + var output, diagnosticOutput bytes.Buffer + outcome := app.Execute(context.Background(), []string{"--verbose", "promptkit", "list", "prompts"}, commandkit.Streams{Out: &output, Err: &diagnosticOutput}) + if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, providerErr) { t.Fatalf("provider failure outcome = %#v", outcome) } + warningAt := strings.Index(diagnosticOutput.String(), "catalog warning") + explanationAt := strings.Index(diagnosticOutput.String(), "PromptKit lookup context") + if warningAt < 0 || explanationAt < 0 || warningAt >= explanationAt || strings.Count(diagnosticOutput.String(), "catalog warning") != 1 { + t.Fatalf("warning/native terminal context were not independently ordered once: %q", diagnosticOutput.String()) + } ctx, cancel := context.WithCancel(context.Background()) canceled := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { cancel() return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing", Version: testStringPointer("1")}}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil }})) - var output bytes.Buffer - outcome = canceled.Execute(ctx, []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: &output, Err: io.Discard}) - if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, context.Canceled) || !hasCatalogWarning(outcome.Diagnostics) || output.Len() != 0 { - t.Fatalf("post-provider cancellation outcome = %#v output=%q", outcome, output.String()) + output.Reset() + diagnosticOutput.Reset() + outcome = canceled.Execute(ctx, []string{"--verbose", "promptkit", "list", "prompts"}, commandkit.Streams{Out: &output, Err: &diagnosticOutput}) + if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, context.Canceled) || !strings.Contains(diagnosticOutput.String(), "catalog warning") || output.Len() != 0 { + t.Fatalf("post-provider cancellation outcome = %#v output=%q err=%q", outcome, output.String(), diagnosticOutput.String()) } writeErr := errors.New("catalog report writer failed") @@ -752,13 +750,96 @@ func TestCatalogFailuresRetainDiagnosticsAndDeliveryState(t *testing.T) { return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing", Version: testStringPointer("1")}}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil }})) outcome := writerApp.Execute(context.Background(), []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: test.writer, Err: io.Discard}) - if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.DeliveryError, test.want) || !errors.Is(outcome.Cause, test.want) || !outcome.ReportIncomplete || !hasCatalogWarning(outcome.Diagnostics) { + if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Presentation.Output.WriteError, test.want) || outcome.Presentation.Output.State != commandkit.DeliveryIncomplete { t.Fatalf("catalog delivery outcome = %#v", outcome) } }) } } +func TestCatalogAndInspectionRespectSuccessfulReportingModes(t *testing.T) { + for _, mode := range []string{"ordinary", "quiet", "verbose", "debug"} { + t.Run(mode, func(t *testing.T) { + calls := 0 + suite := Suite[struct{}, struct{}]{ + ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { + calls++ + return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing"}}}, nil + }, + InspectPrompt: func(context.Context, PromptRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.PromptInspection], error) { + calls++ + return commandkit.Acquired[*promptkit.PromptInspection]{Value: &promptkit.PromptInspection{PromptID: "briefing"}}, nil + }, + } + app := newPromptTestApp(t, Command(suite)) + args := []string{"promptkit", "list", "prompts"} + if mode != "ordinary" { + args = append([]string{"--" + mode}, args...) + } + var output, diagnosticOutput bytes.Buffer + outcome := app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: &diagnosticOutput}) + if outcome.Kind != commandkit.OutcomeSuccess || calls != 1 { + t.Fatalf("%s listing = %#v after %d calls", mode, outcome, calls) + } + if mode == "quiet" { + if output.Len() != 0 { + t.Fatalf("quiet listing wrote result output %q", output.String()) + } + } else if !strings.Contains(output.String(), "briefing") { + t.Fatalf("%s listing omitted result: %q", mode, output.String()) + } + if mode == "debug" && !strings.Contains(diagnosticOutput.String(), "DEBUG:") { + t.Fatalf("debug listing omitted core debug events: %q", diagnosticOutput.String()) + } + + output.Reset() + diagnosticOutput.Reset() + args = []string{"promptkit", "show", "prompt", "briefing"} + if mode != "ordinary" { + args = append([]string{"--" + mode}, args...) + } + outcome = app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: &diagnosticOutput}) + if outcome.Kind != commandkit.OutcomeSuccess || calls != 2 { + t.Fatalf("%s inspection = %#v after %d callbacks", mode, outcome, calls) + } + if mode == "quiet" { + if output.Len() != 0 { + t.Fatalf("quiet inspection wrote result output %q", output.String()) + } + } else if !strings.Contains(output.String(), "Prompt ID: \"briefing\"") { + t.Fatalf("%s inspection omitted result: %q", mode, output.String()) + } + }) + } +} + +func TestQuietInspectionStillFindsReportEncodingFailure(t *testing.T) { + called := false + app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{InspectProfile: func(context.Context, ProfileRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.ProfileInspection], error) { + called = true + return commandkit.Acquired[*promptkit.ProfileInspection]{Value: &promptkit.ProfileInspection{ + ProfileID: "broken", + EffectiveModelParams: promptkit.ExecutionTarget{ExtraParams: map[string]any{"unsupported": func() {}}}, + }}, nil + }})) + result := &countingWriter{} + var diagnosticOutput bytes.Buffer + outcome := app.Execute(context.Background(), []string{"--quiet", "promptkit", "show", "profile", "broken"}, commandkit.Streams{Out: result, Err: &diagnosticOutput}) + if !called || result.calls != 0 { + t.Fatalf("quiet rendering called=%t destination writes=%d", called, result.calls) + } + if outcome.Kind != commandkit.OutcomeOperational || outcome.Status != 1 || outcome.Presentation.Output.RenderError == nil { + t.Fatalf("quiet serialization failure outcome = %#v diagnostics=%q", outcome, diagnosticOutput.String()) + } +} + +type countingWriter struct{ calls int } + +func (writer *countingWriter) Write(p []byte) (int, error) { + writer.calls++ + return len(p), nil +} + func TestCommandUsesInstalledNamespaceCollisionChecks(t *testing.T) { command := Command(Suite[struct{}, struct{}]{InspectPrompt: func(context.Context, PromptRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.PromptInspection], error) { return commandkit.Acquired[*promptkit.PromptInspection]{}, nil @@ -1055,7 +1136,7 @@ func TestDefinitionFormatPreflightAndReportDeliveryFailure(t *testing.T) { writeErr := errors.New("report writer failed") outcome = app.Execute(context.Background(), []string{"promptkit", "show", "profile", "default"}, commandkit.Streams{Out: partialReportWriter{err: writeErr}, Err: io.Discard}) - if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, writeErr) || outcome.DeliveryError != writeErr || !outcome.ReportIncomplete || calls != 1 { + if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Presentation.Output.WriteError, writeErr) || outcome.Presentation.Output.State != commandkit.DeliveryIncomplete || calls != 1 { t.Fatalf("report delivery outcome = %#v calls=%d", outcome, calls) } }