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.
|
ownership of error handling are selected.
|
||||||
OD-3's failure presentation and retention boundaries 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
|
OD-4's delivery lifecycle, write-failure handling, and quiet-mode rendering are
|
||||||
selected. Exact API names, signatures, and representations remain to be
|
selected. The consumer API signatures and presentation-result model below are
|
||||||
finalized. Implementation has not started.
|
also selected. Implementation has not started; architectural contract
|
||||||
|
reconciliation and maintained implementation examples remain work to complete.
|
||||||
|
|
||||||
## Purpose
|
## 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
|
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
|
include detail from a Go error in that explanation. CommandKit presents the
|
||||||
supplied explanation without automatically deriving text from an application
|
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
|
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,
|
||||||
@@ -189,7 +190,7 @@ deduplication: separate events may legitimately produce identical messages.
|
|||||||
Constructing a failure result attaches explanatory content without emitting or
|
Constructing a failure result attaches explanatory content without emitting or
|
||||||
independently scheduling an Error. Returning that result hands the explanation
|
independently scheduling an Error. Returning that result hands the explanation
|
||||||
to CommandKit for terminal delivery through the invocation's reporting facility.
|
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:
|
explanations. For example:
|
||||||
|
|
||||||
| Handler action | Presentation |
|
| 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,
|
queue, reporting lifecycle state, and bounded delivery-failure information,
|
||||||
alongside the temporary parser state and returned explanations described above.
|
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
|
Provide one reporting facility per invocation for substantive output, immediate
|
||||||
messages, and end-of-run diagnostics. The consumer API is chosen for its intended
|
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
|
long-term design; with no current consumers, preserving existing API shapes
|
||||||
carries no weight in this decision.
|
carries no weight in this decision.
|
||||||
|
|
||||||
The facility exposes interfaces appropriate to each need:
|
The following declarations specify the target API and are not yet implemented.
|
||||||
|
|
||||||
| 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 reporter owns presentation: output visibility, progress settings, prefixes,
|
The reporter owns presentation: output visibility, progress settings, prefixes,
|
||||||
ordering, and delivery bookkeeping. Its views share invocation state, allowing
|
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
|
application-defined interface. Domain libraries can continue returning
|
||||||
diagnostic data for the handler to submit, independently of CommandKit.
|
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
|
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`.
|
||||||
The returned outcome carries terminal explanations, with other retention limited
|
Terminal explanations accompany returned outcomes and are delivered through the
|
||||||
to the presentation needs described above. Caller-selected destinations belong
|
same facility. Caller-selected destinations remain at `App.Execute`; handlers
|
||||||
at the execution boundary; the handler-facing API routes output through the
|
use `Invocation.Reporter()` in place of raw invocation streams.
|
||||||
reporter instead of requiring a parallel raw-stream API.
|
|
||||||
|
|
||||||
Immediate methods expose delivery errors and the reporter retains delivery
|
### Invocation reporter and structured detail
|
||||||
failure state for automatic finalization, as selected in OD-4. Standard writer
|
|
||||||
operations remain compatible with `io.Writer`. Exact API names, signatures, and
|
```go
|
||||||
outcome representations will express these behaviors through the selected
|
func (inv Invocation) Reporter() *Reporter
|
||||||
consumer-facing structure.
|
|
||||||
|
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
|
## Upstream diagnostic ownership
|
||||||
|
|
||||||
@@ -561,10 +748,10 @@ rules.
|
|||||||
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`.
|
||||||
3. Finalize the unified reporting API and flag contracts, including interactions
|
3. Implement the selected unified reporting API and flag contracts, including
|
||||||
with command and flag aliases, following OD-1 through OD-4 above. Replace split
|
interactions with command and flag aliases, following OD-1 through OD-4 above.
|
||||||
handler submission paths and adapt core and integration callers. Account for
|
Replace split handler submission paths and adapt core and integration callers.
|
||||||
flag collisions and document changed public contracts.
|
Account for flag collisions and document changed public contracts.
|
||||||
4. Implement the selected behavior across core and the integrations. Preserve
|
4. Implement the selected behavior across core and the integrations. Preserve
|
||||||
upstream rendering and semantics while applying the chosen visibility policy.
|
upstream rendering and semantics while applying the chosen visibility policy.
|
||||||
5. Verify classification, flag selection, suppression, terminal explanations,
|
5. Verify classification, flag selection, suppression, terminal explanations,
|
||||||
@@ -593,6 +780,10 @@ rules.
|
|||||||
that fail without writing and continued terminal delivery through a healthy
|
that fail without writing and continued terminal delivery through a healthy
|
||||||
diagnostic destination. Cover usage-status preservation on presentation
|
diagnostic destination. Cover usage-status preservation on presentation
|
||||||
failure, all category destinations, and usage guidance under quiet mode.
|
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
|
6. Before changing architectural contracts, reconcile the selected error-handling
|
||||||
boundary with ADR 0005's existing outcome and error-identity commitments and
|
boundary with ADR 0005's existing outcome and error-identity commitments and
|
||||||
the corresponding commitments in the [architecture policy](../policy/architecture.md).
|
the corresponding commitments in the [architecture policy](../policy/architecture.md).
|
||||||
@@ -600,10 +791,8 @@ rules.
|
|||||||
the CLI reference, consumer guidance, and release documentation when
|
the CLI reference, consumer guidance, and release documentation when
|
||||||
implemented. Exact reporting APIs and flag syntax remain API/CLI contracts.
|
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
|
OD-1 through OD-4 and the consumer API signatures are selected. Reconcile the
|
||||||
reporter and outcome API, including simple and structured terminal explanations,
|
architectural contracts noted above before changing them, and turn the target-API
|
||||||
deferred submission, and delivery-failure observations. Review complete examples
|
examples into maintained, executable consumers as implementation proceeds.
|
||||||
against the selected behavior and reconcile the architectural contracts noted
|
|
||||||
above.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user