From 085c296d890ec4ec9c75076b278bce5082f1f668 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sat, 24 Oct 2026 01:19:16 +0000 Subject: [PATCH] Define terminal failure presentation for invocation reporting --- docs/roadmap/reporting.md | 78 +++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/docs/roadmap/reporting.md b/docs/roadmap/reporting.md index f7e6c64..1377483 100644 --- a/docs/roadmap/reporting.md +++ b/docs/roadmap/reporting.md @@ -3,9 +3,9 @@ Status: initial planning. Message categories, all four output modes, uniform treatment of help and version, message prefixes, and OD-1's flag grammar and scope (including independent Progress control) are selected. -OD-2's unified reporting direction is selected. Exact API signatures, failure -presentation, retention, and delivery mechanics remain open. Implementation has -not started. +OD-2's unified reporting direction and the terminal failure model are selected. +Exact API signatures, failure explanation details, retention, and delivery +mechanics remain open. Implementation has not started. ## Purpose @@ -46,7 +46,8 @@ There is currently no shared verbosity policy or invocation reporter API. Distinguish substantive application output from diagnostic message categories: - **Substantive output (Normal):** The requested report or command result. -- **Error:** The invocation failed and will return a non-zero status code. +- **Error:** Terminal presentation of an unrecoverable invocation failure, + resulting in a non-zero status code. - **Warning:** Something went wrong or deserves attention, but the invocation can still succeed. A recovered problem is a warning. - **Info:** Informational context and useful explanation, such as which profile @@ -76,12 +77,9 @@ Applications and integrations are not required to generate messages in any category. Enabling a category adds no output when there are no messages in it; other output remains subject to its own visibility rules. -Error presentation is tied to invocation failure. Prefer deriving error -diagnostics from a failed outcome, which may carry detailed explanations. Avoid -an unrestricted `Error(...)` reporting method that permits an error message -followed by a successful outcome. The application still controls process exit -under the existing outcome contract; the returned outcome must consistently -represent the failure and its non-zero status. +Error presentation follows the terminal failure model below. Problems from +which the invocation recovers are warnings, even when an attempted operation +failed internally. Apply the same policy to help and version. There are no command-specific exceptions for successful output: @@ -107,6 +105,37 @@ Streaming Progress messages use the diagnostic channel so they do not corrupt substantive output such as JSON or YAML. Progress presentation details remain part of the consumer API and delivery design below. +### Terminal failure model + +During execution, applications can produce substantive output, Progress, Info, +Debug, and Warnings. An unrecoverable failure ends the handler by returning a +failed outcome. There is no standalone `report.Error(...)` method. Detailed +error explanations accompany the returned failure and are presented as part of +that terminal step. + +CommandKit finalizes reporting, presents the terminal failure explanation, and +returns the non-zero outcome to the application entry point. It accepts no +further normal output for that invocation after terminal error presentation. +The same boundary applies to core routing and parsing failures that prevent +handler execution. The application entry point retains ownership of process +exit; CommandKit ends the invocation and returns its outcome. + +For example, a handler returns immediately on an unrecoverable failure: + +```go +result, err := performOperation() +if err != nil { + return commandkit.Failure(err) +} +// Continue normal reporting only after successful operation. +``` + +Output streamed before the failure remains visible according to its selected +mode; terminal failure does not withdraw earlier bytes. Deferred messages, +cleanup writes, and delivery failures must respect the end of normal reporting. +Their finalization and late-write behavior remain OD-4 details. A terminal +failure may have multiple explanatory records, all belonging to that failure. + ## Selected flag grammar and scope ### Mode selection and resets @@ -300,10 +329,10 @@ diagnostic data for the handler to submit, independently of CommandKit. The returned outcome owns the operation result: success, failure, or an application domain classification. CommandKit presents a failed outcome through -the same reporting facility. Error explanations must be explicitly associated -with the failure they describe; an unrestricted `Error(...)` method that permits -success afterward is outside this direction. Typed upstream diagnostic submission -must preserve that relationship as well. +the same reporting facility as the terminal step. Error explanations belong to +that returned failure, including rich upstream explanations. Typed diagnostic +submission must preserve this relationship instead of permitting independently +emitted Error messages during normal execution. Use the reporter as the common diagnostic submission path, replacing the earlier proposal to split submission between a reporter and `Outcome.Diagnostics`. @@ -349,13 +378,15 @@ rules. 4. Implement the selected behavior across core and the integrations. Preserve upstream rendering and semantics while applying the chosen visibility policy. 5. Verify classification, flag selection, suppression, retained diagnostics, - ordering, duplicate prevention, Error/non-zero-outcome consistency, debug + ordering, duplicate prevention, terminal Error/non-zero-outcome consistency, debug visibility, and write-failure handling with focused behavioral tests and maintained consumer examples. Cover mode resets, mixed canonical/alias ordering, help and discovery contexts, and core parser events presented according to the final mode. Cover explicit progress overrides, debug's progress default, quiet suppression, progress aliases, message prefixes, - and preservation of native multiline diagnostics. + and preservation of native multiline diagnostics. Verify the end of normal + reporting after terminal error presentation, including deferred and late + writes under the selected OD-4 behavior. 6. Update GoDoc, the CLI reference, consumer guidance, and release documentation when implemented. Review whether any resulting architectural change needs an ADR; exact reporting APIs and flag syntax remain API/CLI contracts. @@ -364,12 +395,11 @@ rules. ### OD-3: Failure presentation and diagnostic retention -How does the API enforce that Error diagnostics accompany a failed invocation -with a non-zero status, including application domain outcomes? How should -failure explanations and the operation outcome be associated to avoid -inconsistent caller-supplied combinations? How are recovered upstream problems represented as warnings -while preserving their native context? -Which contextual records belong to an error explanation? How are mixed upstream +The terminal failure model is selected above. How are detailed explanations +attached to the returned failure, including application domain failures? How +are recovered upstream problems represented as warnings while preserving their +native context? Which contextual records belong to a terminal error explanation? +How are mixed upstream diagnostic batches filtered while preserving useful native rendering? What is retained when presentation is suppressed, and how is duplicate presentation prevented without hiding unrelated failures? @@ -379,7 +409,9 @@ diagnostics? What presentation should streaming Progress use? ### OD-4: Delivery lifecycle and quiet execution How are immediate and deferred messages ordered and finalized, including when -the operation fails? How do callers observe and react to write failures? What +the operation fails? How do deferred messages, cleanup writes, and attempts to +use the reporter after terminal failure respect the end of normal reporting? +How do callers observe and react to write failures? What delivery state represents intentional suppression? Does quiet mode discard rendered bytes or skip report rendering, and how does that choice preserve operation semantics and failure detection? Which streaming behavior becomes