Clarify application error handling and reporting ownership

This commit is contained in:
2026-10-24 01:50:38 +00:00
parent 085c296d89
commit ec29d658b7

View File

@@ -3,7 +3,8 @@
Status: initial planning. Message categories, all four output modes, uniform Status: initial planning. Message categories, all four output modes, uniform
treatment of help and version, message prefixes, and OD-1's flag grammar and treatment of help and version, message prefixes, and OD-1's flag grammar and
scope (including independent Progress control) are selected. scope (including independent Progress control) are selected.
OD-2's unified reporting direction and the terminal failure model are selected. OD-2's unified reporting direction, the terminal failure model, and application
ownership of error handling are selected.
Exact API signatures, failure explanation details, retention, and delivery Exact API signatures, failure explanation details, retention, and delivery
mechanics remain open. Implementation has not started. mechanics remain open. Implementation has not started.
@@ -19,6 +20,30 @@ straightforward. Design reporting conveniences around the shared output policy
and invocation lifecycle. Applications continue to own their report content, and invocation lifecycle. Applications continue to own their report content,
domain behavior, and operation outcomes. domain behavior, and operation outcomes.
## CLI presentation and error-handling ownership
Applications decide how to handle errors from their operations: recover, retry,
continue silently, report a Warning, or end the invocation with a failure.
A Go `error` does not determine a CLI message category. Warnings may describe
issues with or without an underlying Go error, and handling an error does not
require emitting a message.
CommandKit handles the presentation the application requests: message category,
visibility, formatting, timing, and delivery. For terminal application failures,
the application supplies a non-zero outcome and an operator-facing explanation.
Reporting does not require the underlying Go error or undertake its tracking,
recovery, or identity preservation.
A convenience helper that explicitly presents `err.Error()` as a Warning could
fit this boundary. Such a helper would only convert the supplied error to a
message at the application's request; its inclusion and signature remain open.
CommandKit retains responsibility for handling its own errors, including
routing, parsing, and reporting failures. Integration-owned commands similarly
adapt their upstream APIs into deliberate command outcomes and presentations.
This boundary governs application error handling; ordinary Go error returns
from CommandKit APIs and writers remain available where appropriate.
## Current baseline ## Current baseline
[`Streams` and `Invocation`](../../identity.go) expose caller-selected output [`Streams` and `Invocation`](../../identity.go) expose caller-selected output
@@ -49,7 +74,7 @@ Distinguish substantive application output from diagnostic message categories:
- **Error:** Terminal presentation of an unrecoverable invocation failure, - **Error:** Terminal presentation of an unrecoverable invocation failure,
resulting in a non-zero status code. resulting in a non-zero status code.
- **Warning:** Something went wrong or deserves attention, but the invocation - **Warning:** Something went wrong or deserves attention, but the invocation
can still succeed. A recovered problem is a warning. can still succeed. The application chooses whether to report such an issue.
- **Info:** Informational context and useful explanation, such as which profile - **Info:** Informational context and useful explanation, such as which profile
was selected or a final operation summary. was selected or a final operation summary.
- **Progress:** Streaming updates about work underway, such as items processed. - **Progress:** Streaming updates about work underway, such as items processed.
@@ -77,9 +102,8 @@ 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; category. Enabling a category adds no output when there are no messages in it;
other output remains subject to its own visibility rules. other output remains subject to its own visibility rules.
Error presentation follows the terminal failure model below. Problems from Error presentation follows the terminal failure model below. An application
which the invocation recovers are warnings, even when an attempted operation may report a recovered problem as a Warning or continue without a message.
failed internally.
Apply the same policy to help and version. There are no command-specific Apply the same policy to help and version. There are no command-specific
exceptions for successful output: exceptions for successful output:
@@ -120,15 +144,12 @@ The same boundary applies to core routing and parsing failures that prevent
handler execution. The application entry point retains ownership of process handler execution. The application entry point retains ownership of process
exit; CommandKit ends the invocation and returns its outcome. exit; CommandKit ends the invocation and returns its outcome.
For example, a handler returns immediately on an unrecoverable failure: For example, if an export operation fails and the application decides it cannot
continue, the handler returns a non-zero outcome with an explicit explanation
```go such as "Unable to export the requested report." The application can choose to
result, err := performOperation() include detail from a Go error in that explanation. CommandKit presents the
if err != nil { supplied explanation without automatically deriving text from an application
return commandkit.Failure(err) error. The exact failure-construction API remains open under OD-3.
}
// Continue normal reporting only after successful operation.
```
Output streamed before the failure remains visible according to its selected Output streamed before the failure remains visible according to its selected
mode; terminal failure does not withdraw earlier bytes. Deferred messages, mode; terminal failure does not withdraw earlier bytes. Deferred messages,
@@ -295,9 +316,9 @@ output is not part of the proposed direction.
Keep errors understandable in every mode, including source locations and Keep errors understandable in every mode, including source locations and
related explanatory context. Unrelated warnings and informational messages related explanatory context. Unrelated warnings and informational messages
remain subject to the selected mode. Retain suppressed diagnostics for remain subject to the selected mode. Whether to retain submitted messages for
programmatic inspection rather than equating suppression with loss of evidence. programmatic inspection, including suppressed messages, remains open under
The exact representation of retained and delivered diagnostics remains open. OD-3. This concerns presentation records, not application error tracking.
## Selected consumer reporting API direction ## Selected consumer reporting API direction
@@ -332,7 +353,8 @@ application domain classification. CommandKit presents a failed outcome through
the same reporting facility as the terminal step. Error explanations belong to the same reporting facility as the terminal step. Error explanations belong to
that returned failure, including rich upstream explanations. Typed diagnostic that returned failure, including rich upstream explanations. Typed diagnostic
submission must preserve this relationship instead of permitting independently submission must preserve this relationship instead of permitting independently
emitted Error messages during normal execution. emitted Error messages during normal execution. Application failures carry
explicit status and presentation content; they do not require a Go error cause.
Use the reporter as the common diagnostic submission path, replacing the earlier Use the reporter as the common diagnostic submission path, replacing the earlier
proposal to split submission between a reporter and `Outcome.Diagnostics`. proposal to split submission between a reporter and `Outcome.Diagnostics`.
@@ -354,9 +376,12 @@ those capabilities through the shared CommandKit presentation policy.
Support consistent reporting of ConfigKit errors and warnings as its adopted Support consistent reporting of ConfigKit errors and warnings as its adopted
public API evolves. This roadmap does not assume that a future warning API public API evolves. This roadmap does not assume that a future warning API
already exists or prescribe its upstream shape. Preserve useful native already exists or prescribe its upstream shape. Integration-owned commands
diagnostic detail and supported error identity, and coordinate returned errors select the outcome and submit useful native diagnostic detail for presentation.
with diagnostics that already explain them. Core presents those records under the shared policy; it does not infer an
application's recovery decisions or classify arbitrary Go errors. Applications
using upstream libraries directly retain the same choice over outcomes and
messages as for their other operations.
Debug output follows the same upstream disclosure and safe-rendering contracts. Debug output follows the same upstream disclosure and safe-rendering contracts.
Enabling debug does not authorize secret disclosure or bypass owning-library Enabling debug does not authorize secret disclosure or bypass owning-library
@@ -368,6 +393,8 @@ rules.
immediate and deferred diagnostics, failure after partial output, help and immediate and deferred diagnostics, failure after partial output, help and
version, and integration diagnostics in every mode. Include applications that version, and integration diagnostics in every mode. Include applications that
emit no Progress messages and the distinction between Info and Progress. emit no Progress messages and the distinction between Info and Progress.
Cover silent recovery, an explicit Warning with or without a Go error, and a
terminal failure with an explicit explanation and no Go error cause.
2. Review current diagnostic batching, parsing, help, outcome presentation, and 2. Review current diagnostic batching, parsing, help, outcome presentation, and
writer ownership to identify the smallest shared changes. Include invocation writer ownership to identify the smallest shared changes. Include invocation
failures that occur before a handler receives its `Invocation`. failures that occur before a handler receives its `Invocation`.
@@ -388,23 +415,33 @@ rules.
reporting after terminal error presentation, including deferred and late reporting after terminal error presentation, including deferred and late
writes under the selected OD-4 behavior. writes under the selected OD-4 behavior.
6. Update GoDoc, the CLI reference, consumer guidance, and release documentation 6. Update GoDoc, the CLI reference, consumer guidance, and release documentation
when implemented. Review whether any resulting architectural change needs an when implemented. Reconcile the selected error-handling boundary with
ADR; exact reporting APIs and flag syntax remain API/CLI contracts. ADR 0005's existing outcome and error-identity commitments before changing
those contracts. Record any resulting architectural change in a new ADR;
exact reporting APIs and flag syntax remain API/CLI contracts.
## Open Decisions ## Open Decisions
### OD-3: Failure presentation and diagnostic retention ### OD-3: Failure presentation and diagnostic retention
The terminal failure model is selected above. How are detailed explanations The terminal failure model and application error-handling boundary are selected
attached to the returned failure, including application domain failures? How above. The remaining questions concern explicit presentation content:
are recovered upstream problems represented as warnings while preserving their
native context? Which contextual records belong to a terminal error explanation? - How does a handler supply a non-zero outcome with a simple explanation or
How are mixed upstream multiple structured explanatory records, including application domain failures?
diagnostic batches filtered while preserving useful native rendering? What is - How do callers and integration adapters identify the context belonging to a
retained when presentation is suppressed, and how is duplicate presentation terminal explanation, and present mixed diagnostic batches while preserving
prevented without hiding unrelated failures? useful native rendering and applying visibility to independent messages?
How do adapters apply the selected prefixes to mixed or already-labelled native - Which submitted messages, if any, are retained for programmatic inspection,
diagnostics? What presentation should streaming Progress use? including when suppressed? How is that observation exposed separately from
message submission?
- How is duplicate presentation avoided, and how do adapters apply prefixes to
mixed or already-labelled native diagnostics?
- What presentation should streaming Progress use? Is a convenience helper for
explicitly presenting `err.Error()` as a Warning useful enough to include?
Application error classification, recovery, and cause tracking are outside
these questions. Delivery failures produced by reporting remain under OD-4.
### OD-4: Delivery lifecycle and quiet execution ### OD-4: Delivery lifecycle and quiet execution