Specify invocation reporting API signatures
This commit is contained in:
@@ -7,8 +7,9 @@ OD-2's unified reporting direction, the terminal failure model, and application
|
||||
ownership of error handling are selected.
|
||||
OD-3's failure presentation and retention boundaries are selected.
|
||||
OD-4's delivery lifecycle, write-failure handling, and quiet-mode rendering are
|
||||
selected. Exact API names, signatures, and representations remain to be
|
||||
finalized. Implementation has not started.
|
||||
selected. The consumer API signatures and presentation-result model below are
|
||||
also selected. Implementation has not started; architectural contract
|
||||
reconciliation and maintained implementation examples remain work to complete.
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -158,7 +159,7 @@ continue, the handler returns a non-zero outcome with an explicit explanation
|
||||
such as "Unable to export the requested report." The application can choose to
|
||||
include detail from a Go error in that explanation. CommandKit presents the
|
||||
supplied explanation without automatically deriving text from an application
|
||||
error. Exact API names and signatures remain to be finalized.
|
||||
error. The selected API below provides failure constructors for this purpose.
|
||||
|
||||
Output streamed before the failure remains visible according to its selected
|
||||
mode; terminal failure does not withdraw earlier bytes. Deferred messages,
|
||||
@@ -189,7 +190,7 @@ deduplication: separate events may legitimately produce identical messages.
|
||||
Constructing a failure result attaches explanatory content without emitting or
|
||||
independently scheduling an Error. Returning that result hands the explanation
|
||||
to CommandKit for terminal delivery through the invocation's reporting facility.
|
||||
The concrete API must express this distinction for both simple and structured
|
||||
The selected API expresses this distinction for both simple and structured
|
||||
explanations. For example:
|
||||
|
||||
| Handler action | Presentation |
|
||||
@@ -473,26 +474,14 @@ delivery. Do not retain suppressed content. Delivery needs only the deferred
|
||||
queue, reporting lifecycle state, and bounded delivery-failure information,
|
||||
alongside the temporary parser state and returned explanations described above.
|
||||
|
||||
## Selected consumer reporting API direction
|
||||
## Selected consumer reporting API
|
||||
|
||||
Provide one reporting facility per invocation for substantive output, immediate
|
||||
messages, and end-of-run diagnostics. The consumer API is chosen for its intended
|
||||
long-term design; with no current consumers, preserving existing API shapes
|
||||
carries no weight in this decision.
|
||||
|
||||
The facility exposes interfaces appropriate to each need:
|
||||
|
||||
| Need | Selected direction | Illustrative shape |
|
||||
| --- | --- | --- |
|
||||
| Substantive results | An ordinary `io.Writer`, usable by encoders and native report writers. | `inv.Reporter().Output()` |
|
||||
| Immediate messages | Categorized methods applying visibility and presentation automatically. | `Info`, `Warn`, `Debug`, `Progress` |
|
||||
| End-of-run diagnostics | An explicit deferred view sharing the invocation's reporting state. | `report.AtEnd().Warn(message)` |
|
||||
| Rich upstream diagnostics | Typed submission preserving severity, context, and native rendering. | Exact API remains open. |
|
||||
|
||||
These method names illustrate the selected direction; exact names and signatures
|
||||
remain to be finalized. Progress is streaming output, distinct from deferred
|
||||
informational summaries. Applications need not implement categories they do not
|
||||
use or check verbosity before reporting.
|
||||
The following declarations specify the target API and are not yet implemented.
|
||||
|
||||
The reporter owns presentation: output visibility, progress settings, prefixes,
|
||||
ordering, and delivery bookkeeping. Its views share invocation state, allowing
|
||||
@@ -501,26 +490,224 @@ be coordinated in one place. Helpers may receive the reporter or a narrow
|
||||
application-defined interface. Domain libraries can continue returning
|
||||
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 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. 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
|
||||
proposal to split submission between a reporter and `Outcome.Diagnostics`.
|
||||
The returned outcome carries terminal explanations, with other retention limited
|
||||
to the presentation needs described above. Caller-selected destinations belong
|
||||
at the execution boundary; the handler-facing API routes output through the
|
||||
reporter instead of requiring a parallel raw-stream API.
|
||||
Terminal explanations accompany returned outcomes and are delivered through the
|
||||
same facility. Caller-selected destinations remain at `App.Execute`; handlers
|
||||
use `Invocation.Reporter()` in place of raw invocation streams.
|
||||
|
||||
Immediate methods expose delivery errors and the reporter retains delivery
|
||||
failure state for automatic finalization, as selected in OD-4. Standard writer
|
||||
operations remain compatible with `io.Writer`. Exact API names, signatures, and
|
||||
outcome representations will express these behaviors through the selected
|
||||
consumer-facing structure.
|
||||
### Invocation reporter and structured detail
|
||||
|
||||
```go
|
||||
func (inv Invocation) Reporter() *Reporter
|
||||
|
||||
type Reporter struct {
|
||||
// Private invocation state.
|
||||
}
|
||||
|
||||
func (r *Reporter) Output() io.Writer
|
||||
func (r *Reporter) RenderOutput(write func(io.Writer) error) error
|
||||
|
||||
func (r *Reporter) Info(message string, details ...Detail) error
|
||||
func (r *Reporter) Warn(message string, details ...Detail) error
|
||||
func (r *Reporter) Debug(message string, details ...Detail) error
|
||||
func (r *Reporter) Progress(message string, details ...Detail) error
|
||||
|
||||
func (r *Reporter) AtEnd() DeferredReporter
|
||||
|
||||
type Detail func(io.Writer) error
|
||||
```
|
||||
|
||||
`Output()` supports ordinary writers and encoders. `RenderOutput()` coordinates
|
||||
an entire rendering operation, captures its rendering error, and prevents other
|
||||
reporter operations from interleaving with it. Rendering callbacks write through
|
||||
their supplied writer. Each complete categorized message, including its details,
|
||||
is likewise coordinated as one reporter operation.
|
||||
|
||||
Message strings are literal; consumers use `fmt.Sprintf` when formatting is
|
||||
needed. Initially omit parallel `Infof`, `Warnf`, and similar methods. CommandKit
|
||||
formats the opening message with its category's prefix and a terminating newline.
|
||||
Optional details follow in order with their formatting preserved. An empty
|
||||
opening message produces no heading, prefix, or blank opening line, supporting
|
||||
native renderers that supply a complete, labelled diagnostic:
|
||||
|
||||
```go
|
||||
_ = report.Warn("Using the default profile")
|
||||
_ = report.Warn("Some configuration settings need attention.", writeNativeWarnings)
|
||||
_ = report.Warn("", writeNativeWarnings)
|
||||
```
|
||||
|
||||
These are alternative calls; `writeNativeWarnings` represents an
|
||||
application- or integration-supplied function with signature
|
||||
`func(io.Writer) error`. No generic logging method or severity enumeration is
|
||||
needed to submit messages. Applications need not check verbosity before reporting
|
||||
or implement categories they do not use.
|
||||
|
||||
Immediate methods return rendering or delivery errors, and CommandKit records
|
||||
the corresponding presentation failure even if the caller ignores the error.
|
||||
A caller can inspect the returned error and end its handler immediately.
|
||||
|
||||
### Deferred messages
|
||||
|
||||
```go
|
||||
type DeferredReporter struct {
|
||||
// Private reference to the same invocation state.
|
||||
}
|
||||
|
||||
func (r DeferredReporter) Info(message string, details ...Detail) error
|
||||
func (r DeferredReporter) Warn(message string, details ...Detail) error
|
||||
func (r DeferredReporter) Debug(message string, details ...Detail) error
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
_ = report.AtEnd().Warn("Three optional files were unavailable.")
|
||||
```
|
||||
|
||||
Copies of the deferred view share invocation state. Its methods return submission
|
||||
errors, such as attempting to use a closed reporter. Errors encountered during
|
||||
deferred rendering appear in the final invocation outcome. Progress remains an
|
||||
immediate streaming operation; substantive output uses the main reporter.
|
||||
|
||||
Copy callback lists when accepting a submission. Callers retain ownership of
|
||||
data captured by rendering callbacks and keep that data valid until presentation.
|
||||
|
||||
### Terminal outcomes
|
||||
|
||||
Outcome construction requires no invocation state and emits nothing:
|
||||
|
||||
```go
|
||||
type Explanation struct {
|
||||
Message string
|
||||
Details []Detail
|
||||
}
|
||||
|
||||
type Outcome struct {
|
||||
Kind OutcomeKind
|
||||
Status int
|
||||
Domain string
|
||||
Explanation Explanation
|
||||
Presentation PresentationResult
|
||||
}
|
||||
|
||||
func Success() Outcome
|
||||
func Failure(message string, details ...Detail) Outcome
|
||||
func Usage(message string, details ...Detail) Outcome
|
||||
func Domain(name string, status int) Outcome
|
||||
|
||||
func (o Outcome) WithExplanation(message string, details ...Detail) Outcome
|
||||
```
|
||||
|
||||
`Success` returns a successful outcome with status `0`. `Failure` produces an
|
||||
operational failure with status `1`; `Usage` produces a usage failure with status
|
||||
`2`. `Domain` preserves an application-defined classification and status.
|
||||
`OutcomeKind` retains the success, usage, operational, and domain classifications.
|
||||
|
||||
`WithExplanation` returns a modified outcome value and replaces its terminal
|
||||
explanation. Explanations belong to failed outcomes; successful summaries use
|
||||
Info or substantive output. Ordinary application failure, native failure
|
||||
rendering, and a custom domain failure respectively can be expressed as:
|
||||
|
||||
```go
|
||||
return commandkit.Failure("Unable to export the requested report.")
|
||||
```
|
||||
|
||||
```go
|
||||
return commandkit.Failure("", writeNativeFailure)
|
||||
```
|
||||
|
||||
```go
|
||||
return commandkit.Domain("validation", 3).
|
||||
WithExplanation("The document failed validation.", writeFindings)
|
||||
```
|
||||
|
||||
The named renderers above have signature `func(io.Writer) error` and preserve the
|
||||
owning library's safe diagnostic formatting. They run during terminal delivery,
|
||||
not when constructing the outcome.
|
||||
|
||||
The target outcome has no application `Cause` field. Applications supply their
|
||||
chosen explanation; CommandKit records its own presentation failures separately.
|
||||
The returned outcome retains the terminal explanation, with other retention
|
||||
limited to the presentation needs described above.
|
||||
|
||||
### Presentation observations and closed reporters
|
||||
|
||||
```go
|
||||
type PresentationResult struct {
|
||||
Output DeliveryResult
|
||||
Diagnostics DeliveryResult
|
||||
}
|
||||
|
||||
type DeliveryResult struct {
|
||||
State DeliveryState
|
||||
RenderError error
|
||||
WriteError error
|
||||
}
|
||||
|
||||
type DeliveryState uint8
|
||||
|
||||
const (
|
||||
DeliveryNone DeliveryState = iota
|
||||
DeliverySuppressed
|
||||
DeliveryComplete
|
||||
DeliveryIncomplete
|
||||
)
|
||||
|
||||
var ErrReporterClosed = errors.New("reporter is closed")
|
||||
```
|
||||
|
||||
| State | Meaning |
|
||||
| --- | --- |
|
||||
| `DeliveryNone` | No content was submitted for this destination. |
|
||||
| `DeliverySuppressed` | All submitted content was intentionally suppressed, and rendering succeeded. |
|
||||
| `DeliveryComplete` | All requested delivery completed; other content may have been suppressed. |
|
||||
| `DeliveryIncomplete` | Rendering or destination writing failed. |
|
||||
|
||||
Retain the first rendering failure and first destination-write failure for each
|
||||
destination. This provides bounded evidence without collecting an event history.
|
||||
`Outcome.Presentation` is populated by CommandKit during execution and
|
||||
finalization; it is observation data, not another submission API.
|
||||
|
||||
The invocation supplies the reporter. Consumers neither construct nor finalize
|
||||
it. After closure, message submissions and writer operations return
|
||||
`ErrReporterClosed`, including in quiet mode. The initial API needs no explicit
|
||||
flush method, transcript accessor, verbosity checks, or error-conversion helpers.
|
||||
|
||||
### Application example
|
||||
|
||||
This target-API handler uses an application-owned `exportData` operation that
|
||||
returns a JSON-encodable result with a `UsedDefaults` field:
|
||||
|
||||
```go
|
||||
func runExport(inv commandkit.Invocation) commandkit.Outcome {
|
||||
report := inv.Reporter()
|
||||
|
||||
result, err := exportData(inv.Context)
|
||||
if err != nil {
|
||||
return commandkit.Failure("Unable to export the requested data.")
|
||||
}
|
||||
|
||||
if result.UsedDefaults {
|
||||
_ = report.Warn("Some optional settings used defaults.")
|
||||
}
|
||||
|
||||
if err := report.RenderOutput(func(w io.Writer) error {
|
||||
return json.NewEncoder(w).Encode(result)
|
||||
}); err != nil {
|
||||
return commandkit.Failure("Unable to present the export result.")
|
||||
}
|
||||
|
||||
_ = report.AtEnd().Info("Export completed.")
|
||||
return commandkit.Success()
|
||||
}
|
||||
```
|
||||
|
||||
Ignored immediate reporting errors are still recorded and can make the final
|
||||
invocation unsuccessful. The renderer runs in quiet mode against the discard
|
||||
destination, preserving serialization failures. The deferred summary follows
|
||||
the selected verbosity policy and describes the completed export operation;
|
||||
the final invocation outcome also accounts for presentation failures.
|
||||
|
||||
## Upstream diagnostic ownership
|
||||
|
||||
@@ -561,10 +748,10 @@ rules.
|
||||
2. Review current diagnostic batching, parsing, help, outcome presentation, and
|
||||
writer ownership to identify the smallest shared changes. Include invocation
|
||||
failures that occur before a handler receives its `Invocation`.
|
||||
3. Finalize the unified reporting API and flag contracts, including interactions
|
||||
with command and flag aliases, following OD-1 through OD-4 above. Replace split
|
||||
handler submission paths and adapt core and integration callers. Account for
|
||||
flag collisions and document changed public contracts.
|
||||
3. Implement the selected unified reporting API and flag contracts, including
|
||||
interactions with command and flag aliases, following OD-1 through OD-4 above.
|
||||
Replace split handler submission paths and adapt core and integration callers.
|
||||
Account for flag collisions and document changed public contracts.
|
||||
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, terminal explanations,
|
||||
@@ -593,6 +780,10 @@ rules.
|
||||
that fail without writing and continued terminal delivery through a healthy
|
||||
diagnostic destination. Cover usage-status preservation on presentation
|
||||
failure, all category destinations, and usage guidance under quiet mode.
|
||||
Verify the selected API's empty-heading native rendering, ordered details,
|
||||
deferred submission errors, replacement by `WithExplanation`, and delivery
|
||||
states for each destination. Check bounded first-error observations, rendering
|
||||
through `RenderOutput`, and `ErrReporterClosed` for retained handles in quiet mode.
|
||||
6. Before changing architectural contracts, reconcile the selected error-handling
|
||||
boundary with ADR 0005's existing outcome and error-identity commitments and
|
||||
the corresponding commitments in the [architecture policy](../policy/architecture.md).
|
||||
@@ -600,10 +791,8 @@ rules.
|
||||
the CLI reference, consumer guidance, and release documentation when
|
||||
implemented. Exact reporting APIs and flag syntax remain API/CLI contracts.
|
||||
|
||||
## Remaining design work
|
||||
## Remaining preparation
|
||||
|
||||
OD-1 through OD-4 are resolved. Before implementation, finalize the concrete
|
||||
reporter and outcome API, including simple and structured terminal explanations,
|
||||
deferred submission, and delivery-failure observations. Review complete examples
|
||||
against the selected behavior and reconcile the architectural contracts noted
|
||||
above.
|
||||
OD-1 through OD-4 and the consumer API signatures are selected. Reconcile the
|
||||
architectural contracts noted above before changing them, and turn the target-API
|
||||
examples into maintained, executable consumers as implementation proceeds.
|
||||
|
||||
Reference in New Issue
Block a user