From d5f544d14dc274e3fecc4a8998452a81f90f396a Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Wed, 7 Oct 2026 16:49:56 +0000 Subject: [PATCH] Treat nil diagnostic writers as no-output entries --- docs/roadmap/remediation.md | 3 +++ execute.go | 11 +++++++---- execute_external_test.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index f8d336b..6033fc5 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -63,6 +63,9 @@ architecture. The issues below affect behavior within the implemented structure. - R-06 complete: zero outcomes normalize to success/0 and empty delivery state normalizes to none without altering explicit domain outcomes. +- R-07 complete: nil diagnostic writers are no-output entries, do not count as + delivery attempts, and cannot suppress cause presentation. + ## Findings and priority P1 means a supported invocation is blocked or its report content is corrupted. diff --git a/execute.go b/execute.go index 54edd2b..c645bec 100644 --- a/execute.go +++ b/execute.go @@ -111,11 +111,12 @@ func (a *App) present(outcome Outcome, path []string, streams Streams) Outcome { if len(diagnostics) == 0 { return outcome } + delivered := false for _, diagnostic := range diagnostics { if diagnostic.Write == nil { - outcome = deliveryFailure(outcome, errors.New("diagnostic has no writer")) - return outcome + continue } + delivered = true writer := &checkedWriter{writer: streams.Err} writeErr := diagnostic.Write(writer) if writer.failure != nil { @@ -129,7 +130,9 @@ func (a *App) present(outcome Outcome, path []string, streams Streams) Outcome { return deliveryFailure(outcome, writeErr) } } - outcome.DiagnosticDelivery = DeliveryComplete + if delivered { + outcome.DiagnosticDelivery = DeliveryComplete + } return outcome } @@ -148,7 +151,7 @@ func (a *App) usageLine(path []string) string { func hasErrorDiagnostic(diagnostics []Diagnostic) bool { for _, diagnostic := range diagnostics { - if diagnostic.Severity == configkit.SeverityError { + if diagnostic.Write != nil && diagnostic.Severity == configkit.SeverityError { return true } } diff --git a/execute_external_test.go b/execute_external_test.go index 06ac243..878bfd9 100644 --- a/execute_external_test.go +++ b/execute_external_test.go @@ -469,3 +469,39 @@ func TestExecuteNormalizesOutcomeDefaultsAndPreservesDomainValues(t *testing.T) } } } + +func TestNilDiagnosticWritersHaveNoOutputAndDoNotHideCauses(t *testing.T) { + cause := errors.New("operation failed") + for _, base := range []commandkit.Outcome{commandkit.Success(), commandkit.Domain("needs action", 2, nil), commandkit.Failure(cause)} { + base.Diagnostics = []commandkit.Diagnostic{{}, {Severity: configkit.SeverityWarning}, {Severity: configkit.SeverityError}} + command, err := commandkit.NewCommand(commandkit.CommandSpec[struct{}]{Name: "run", Run: func(commandkit.Invocation, struct{}) commandkit.Outcome { return base }}) + if err != nil { + t.Fatal(err) + } + app, err := commandkit.New(commandkit.Identity{Name: "example"}, command) + if err != nil { + t.Fatal(err) + } + var diagnostics bytes.Buffer + result := app.Execute(context.Background(), []string{"run"}, commandkit.Streams{Err: &diagnostics}) + want := commandkit.DeliveryNone + if base.Cause != nil { + want = commandkit.DeliveryComplete + } + if result.Kind != base.Kind || result.Status != base.Status || result.Domain != base.Domain || result.Cause != base.Cause || result.DeliveryError != nil || result.DiagnosticDelivery != want { + t.Fatalf("result=%#v base=%#v", result, base) + } + if base.Cause == nil && diagnostics.Len() != 0 { + t.Fatalf("unexpected output=%q", diagnostics.String()) + } + if base.Cause != nil && strings.Count(diagnostics.String(), cause.Error()) != 1 { + t.Fatalf("cause output=%q", diagnostics.String()) + } + if base.Cause != nil { + result = app.Execute(context.Background(), []string{"run"}, commandkit.Streams{Err: failingWriter{io.ErrClosedPipe}}) + if !errors.Is(result.Cause, cause) || !errors.Is(result.DeliveryError, io.ErrClosedPipe) || result.DiagnosticDelivery != commandkit.DeliveryIncomplete { + t.Fatalf("failed delivery=%#v", result) + } + } + } +}