Migrate PromptKit inspection reports to the reporter
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Invocation reporting implementation plan
|
# Invocation reporting implementation plan
|
||||||
|
|
||||||
Status: Stages 1–13 complete. Stage 14 is next.
|
Status: Stages 1–14 complete. Stage 15 is next.
|
||||||
|
|
||||||
## Objective and authority
|
## Objective and authority
|
||||||
|
|
||||||
@@ -535,7 +535,7 @@ adapter files; Stage 20 will run the assembled suite.
|
|||||||
|
|
||||||
## Stage 14: Migrate PromptKit listing and inspection commands
|
## Stage 14: Migrate PromptKit listing and inspection commands
|
||||||
|
|
||||||
Status: pending.
|
Status: complete.
|
||||||
|
|
||||||
Primary file: [command.go](../../integrations/ckprompt/command.go) and its
|
Primary file: [command.go](../../integrations/ckprompt/command.go) and its
|
||||||
catalog, definition, and requirements report helpers/tests.
|
catalog, definition, and requirements report helpers/tests.
|
||||||
@@ -557,6 +557,16 @@ Validation: migrate existing report/selection tests; add representative
|
|||||||
successful quiet/verbose/debug execution and native acquisition failure with an
|
successful quiet/verbose/debug execution and native acquisition failure with an
|
||||||
independent warning. Reuse core reporting coverage for universal mechanics.
|
independent warning. Reuse core reporting coverage for universal mechanics.
|
||||||
|
|
||||||
|
PromptKit catalog listing, definition inspection, and requirements inspection
|
||||||
|
now submit provider messages through the reporter and render complete reports
|
||||||
|
inside managed output. Acquisition failures use native terminal context when
|
||||||
|
available and preserve the original cause. Tests cover independent warning and
|
||||||
|
terminal-context delivery, successful ordinary/quiet/verbose/debug execution,
|
||||||
|
quiet encoding failure, cancellation, and result delivery observations. The
|
||||||
|
ckprompt suite cannot run yet because `validation_command.go` and
|
||||||
|
`smoke_command.go` still use the legacy reporting API scheduled for Stage 15;
|
||||||
|
Stage 20 will validate the assembled module.
|
||||||
|
|
||||||
## Stage 15: Migrate request validation and smoke commands
|
## Stage 15: Migrate request validation and smoke commands
|
||||||
|
|
||||||
Status: pending.
|
Status: pending.
|
||||||
|
|||||||
@@ -185,23 +185,23 @@ func newCatalogListCommand[I, S any](suite Suite[I, S], name string) commandkit.
|
|||||||
Run: func(invocation commandkit.Invocation, input *catalogInput[I]) commandkit.Outcome {
|
Run: func(invocation commandkit.Invocation, input *catalogInput[I]) commandkit.Outcome {
|
||||||
switch name {
|
switch name {
|
||||||
case "prompts":
|
case "prompts":
|
||||||
return runCatalogList(invocation, suite, input, suite.ListPrompts, encodePromptCatalog)
|
return runCatalogList(invocation, suite, input, name, suite.ListPrompts, encodePromptCatalog)
|
||||||
case "profiles":
|
case "profiles":
|
||||||
return runCatalogList(invocation, suite, input, suite.ListProfiles, encodeProfileCatalog)
|
return runCatalogList(invocation, suite, input, name, suite.ListProfiles, encodeProfileCatalog)
|
||||||
case "backends":
|
case "backends":
|
||||||
return runCatalogList(invocation, suite, input, suite.ListBackends, encodeProfileCatalog)
|
return runCatalogList(invocation, suite, input, name, suite.ListBackends, encodeProfileCatalog)
|
||||||
case "namespaces":
|
case "namespaces":
|
||||||
list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]string], error) {
|
list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]string], error) {
|
||||||
return suite.ListNamespaces(ctx, NamespaceRequest{Selection: request.Selection, Kinds: input.kinds}, value, state)
|
return suite.ListNamespaces(ctx, NamespaceRequest{Selection: request.Selection, Kinds: input.kinds}, value, state)
|
||||||
}
|
}
|
||||||
return runCatalogList(invocation, suite, input, list, encodeProfileCatalog)
|
return runCatalogList(invocation, suite, input, name, list, encodeProfileCatalog)
|
||||||
case "collections":
|
case "collections":
|
||||||
list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]promptkit.CollectionInfo], error) {
|
list := func(ctx context.Context, request CatalogRequest, value I, state S) (commandkit.Acquired[[]promptkit.CollectionInfo], error) {
|
||||||
return suite.ListCollections(ctx, request.Selection, value, state)
|
return suite.ListCollections(ctx, request.Selection, value, state)
|
||||||
}
|
}
|
||||||
return runCatalogList(invocation, suite, input, list, encodeCollectionCatalog)
|
return runCatalogList(invocation, suite, input, name, list, encodeCollectionCatalog)
|
||||||
default:
|
default:
|
||||||
return definitionFailure(fmt.Errorf("unknown PromptKit catalog %q", name), nil)
|
return definitionFailure("Unable to list the selected PromptKit catalog.", commandkit.Explanation{}, fmt.Errorf("unknown PromptKit catalog %q", name))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -385,12 +385,13 @@ func runCatalogList[T any, I, S any](
|
|||||||
invocation commandkit.Invocation,
|
invocation commandkit.Invocation,
|
||||||
suite Suite[I, S],
|
suite Suite[I, S],
|
||||||
input *catalogInput[I],
|
input *catalogInput[I],
|
||||||
|
catalog string,
|
||||||
list func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[T], error),
|
list func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[T], error),
|
||||||
encode func(T) ([]byte, error),
|
encode func(T) ([]byte, error),
|
||||||
) commandkit.Outcome {
|
) commandkit.Outcome {
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, nil)
|
return definitionFailure("PromptKit catalog listing was canceled.", commandkit.Explanation{}, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var state S
|
var state S
|
||||||
@@ -399,30 +400,35 @@ func runCatalogList[T any, I, S any](
|
|||||||
}
|
}
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, nil)
|
return definitionFailure("PromptKit catalog listing was canceled.", commandkit.Explanation{}, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acquired, err := list(invocation.Context, CatalogRequest{Selection: input.selection, Filter: input.filter}, input.selected, state)
|
acquired, err := list(invocation.Context, CatalogRequest{Selection: input.selection, Filter: input.filter}, input.selected, state)
|
||||||
|
queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
return definitionFailure(fmt.Sprintf("Unable to list PromptKit %s.", catalog), acquired.Explanation, err)
|
||||||
}
|
}
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
return definitionFailure("PromptKit catalog listing was canceled.", acquired.Explanation, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output, err := encode(acquired.Value)
|
_ = invocation.Reporter().RenderOutput(func(writer io.Writer) error {
|
||||||
if err != nil {
|
output, err := encode(acquired.Value)
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
if err != nil {
|
||||||
}
|
return err
|
||||||
return writePromptReport(invocation, output, acquired.Diagnostics)
|
}
|
||||||
|
_, err = writer.Write(output)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return commandkit.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDefinitionInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *definitionInput[I], prompt bool) commandkit.Outcome {
|
func runDefinitionInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *definitionInput[I], prompt bool) commandkit.Outcome {
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, nil)
|
return definitionFailure("PromptKit inspection was canceled.", commandkit.Explanation{}, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var state S
|
var state S
|
||||||
@@ -431,41 +437,43 @@ func runDefinitionInspection[I, S any](invocation commandkit.Invocation, suite S
|
|||||||
}
|
}
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, nil)
|
return definitionFailure("PromptKit inspection was canceled.", commandkit.Explanation{}, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if prompt {
|
if prompt {
|
||||||
request := PromptRequest{Selection: input.selection, ID: input.id}
|
request := PromptRequest{Selection: input.selection, ID: input.id}
|
||||||
acquired, err := suite.InspectPrompt(invocation.Context, request, input.selected, state)
|
acquired, err := suite.InspectPrompt(invocation.Context, request, input.selected, state)
|
||||||
|
queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
return definitionFailure(fmt.Sprintf("Unable to inspect PromptKit prompt %q.", input.id), acquired.Explanation, err)
|
||||||
}
|
}
|
||||||
if acquired.Value == nil {
|
if acquired.Value == nil {
|
||||||
return definitionFailure(errors.New("PromptKit prompt inspection returned nil"), acquired.Diagnostics)
|
return definitionFailure("PromptKit prompt inspection returned no result.", acquired.Explanation, nil)
|
||||||
}
|
}
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
return definitionFailure(fmt.Sprintf("Inspection of PromptKit prompt %q was canceled.", input.id), acquired.Explanation, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return renderPromptReport(invocation, promptReportFromInspection(acquired.Value), input.format, acquired.Diagnostics)
|
return renderPromptReport(invocation, acquired.Value, input.format)
|
||||||
}
|
}
|
||||||
|
|
||||||
request := ProfileRequest{Selection: input.selection, ID: input.id}
|
request := ProfileRequest{Selection: input.selection, ID: input.id}
|
||||||
acquired, err := suite.InspectProfile(invocation.Context, request, input.selected, state)
|
acquired, err := suite.InspectProfile(invocation.Context, request, input.selected, state)
|
||||||
|
queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
return definitionFailure(fmt.Sprintf("Unable to inspect PromptKit profile %q.", input.id), acquired.Explanation, err)
|
||||||
}
|
}
|
||||||
if acquired.Value == nil {
|
if acquired.Value == nil {
|
||||||
return definitionFailure(errors.New("PromptKit profile inspection returned nil"), acquired.Diagnostics)
|
return definitionFailure("PromptKit profile inspection returned no result.", acquired.Explanation, nil)
|
||||||
}
|
}
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
return definitionFailure(fmt.Sprintf("Inspection of PromptKit profile %q was canceled.", input.id), acquired.Explanation, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return renderProfileReport(invocation, profileReportFromInspection(acquired.Value), input.format, acquired.Diagnostics)
|
return renderProfileReport(invocation, acquired.Value, input.format)
|
||||||
}
|
}
|
||||||
|
|
||||||
type promptInputReport struct {
|
type promptInputReport struct {
|
||||||
@@ -624,7 +632,7 @@ func profileReportFromInspection(inspection *promptkit.ProfileInspection) profil
|
|||||||
func runRequirementsInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *requirementsInput[I]) commandkit.Outcome {
|
func runRequirementsInspection[I, S any](invocation commandkit.Invocation, suite Suite[I, S], input *requirementsInput[I]) commandkit.Outcome {
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, nil)
|
return definitionFailure("PromptKit requirements inspection was canceled.", commandkit.Explanation{}, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var state S
|
var state S
|
||||||
@@ -633,56 +641,56 @@ func runRequirementsInspection[I, S any](invocation commandkit.Invocation, suite
|
|||||||
}
|
}
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, nil)
|
return definitionFailure("PromptKit requirements inspection was canceled.", commandkit.Explanation{}, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
request := RequirementsRequest{Selection: input.selection, Inspection: input.inspection}
|
request := RequirementsRequest{Selection: input.selection, Inspection: input.inspection}
|
||||||
acquired, err := suite.InspectRequirements(invocation.Context, request, input.selected, state)
|
acquired, err := suite.InspectRequirements(invocation.Context, request, input.selected, state)
|
||||||
|
queuePromptDiagnostics(invocation.Reporter(), acquired.Diagnostics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
return definitionFailure(fmt.Sprintf("Unable to inspect requirements for PromptKit prompt %q.", input.inspection.PromptID), acquired.Explanation, err)
|
||||||
}
|
}
|
||||||
if acquired.Value == nil {
|
if acquired.Value == nil {
|
||||||
return definitionFailure(errors.New("PromptKit requirements inspection returned nil"), acquired.Diagnostics)
|
return definitionFailure("PromptKit requirements inspection returned no result.", acquired.Explanation, nil)
|
||||||
}
|
}
|
||||||
if invocation.Context != nil {
|
if invocation.Context != nil {
|
||||||
if err := invocation.Context.Err(); err != nil {
|
if err := invocation.Context.Err(); err != nil {
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
return definitionFailure(fmt.Sprintf("Requirements inspection for PromptKit prompt %q was canceled.", input.inspection.PromptID), acquired.Explanation, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output, err := encodePromptYAMLBytes(requirementsReportFromInspection(acquired.Value))
|
_ = invocation.Reporter().RenderOutput(func(writer io.Writer) error {
|
||||||
if err != nil {
|
return encodePromptYAML(writer, requirementsReportFromInspection(acquired.Value))
|
||||||
return definitionFailure(err, acquired.Diagnostics)
|
})
|
||||||
}
|
return commandkit.Success()
|
||||||
return writePromptReport(invocation, output, acquired.Diagnostics)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderPromptReport(invocation commandkit.Invocation, report promptInspectionReport, format string, diagnostics []commandkit.Diagnostic) commandkit.Outcome {
|
func renderPromptReport(invocation commandkit.Invocation, inspection *promptkit.PromptInspection, format string) commandkit.Outcome {
|
||||||
var output bytes.Buffer
|
_ = invocation.Reporter().RenderOutput(func(writer io.Writer) error {
|
||||||
if format == "yaml" {
|
report := promptReportFromInspection(inspection)
|
||||||
if err := encodePromptYAML(&output, report); err != nil {
|
if format == "yaml" {
|
||||||
return definitionFailure(err, diagnostics)
|
return encodePromptYAML(writer, report)
|
||||||
}
|
}
|
||||||
} else {
|
_, err := io.WriteString(writer, formatPromptText(report))
|
||||||
output.WriteString(formatPromptText(report))
|
return err
|
||||||
}
|
})
|
||||||
return writePromptReport(invocation, output.Bytes(), diagnostics)
|
return commandkit.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderProfileReport(invocation commandkit.Invocation, report profileInspectionReport, format string, diagnostics []commandkit.Diagnostic) commandkit.Outcome {
|
func renderProfileReport(invocation commandkit.Invocation, inspection *promptkit.ProfileInspection, format string) commandkit.Outcome {
|
||||||
var output bytes.Buffer
|
_ = invocation.Reporter().RenderOutput(func(writer io.Writer) error {
|
||||||
if format == "yaml" {
|
report := profileReportFromInspection(inspection)
|
||||||
if err := encodePromptYAML(&output, report); err != nil {
|
if format == "yaml" {
|
||||||
return definitionFailure(err, diagnostics)
|
return encodePromptYAML(writer, report)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
text, err := formatProfileText(report)
|
text, err := formatProfileText(report)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return definitionFailure(err, diagnostics)
|
return err
|
||||||
}
|
}
|
||||||
output.WriteString(text)
|
_, err = io.WriteString(writer, text)
|
||||||
}
|
return err
|
||||||
return writePromptReport(invocation, output.Bytes(), diagnostics)
|
})
|
||||||
|
return commandkit.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatPromptText(report promptInspectionReport) string {
|
func formatPromptText(report promptInspectionReport) string {
|
||||||
@@ -771,30 +779,33 @@ func encodePromptYAMLBytes(value any) ([]byte, error) {
|
|||||||
return output.Bytes(), nil
|
return output.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePromptReport(invocation commandkit.Invocation, output []byte, diagnostics []commandkit.Diagnostic) commandkit.Outcome {
|
func queuePromptDiagnostics(reporter *commandkit.Reporter, diagnostics []commandkit.Diagnostic) {
|
||||||
if invocation.Context != nil {
|
deferred := reporter.AtEnd()
|
||||||
if err := invocation.Context.Err(); err != nil {
|
for _, diagnostic := range diagnostics {
|
||||||
return definitionFailure(err, diagnostics)
|
if diagnostic.Write == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch diagnostic.Severity {
|
||||||
|
case commandkit.SeverityInfo:
|
||||||
|
_ = deferred.Info("", diagnostic.Write)
|
||||||
|
case commandkit.SeverityDebug:
|
||||||
|
_ = deferred.Debug("", diagnostic.Write)
|
||||||
|
case commandkit.SeverityProgress:
|
||||||
|
_ = reporter.Progress("", diagnostic.Write)
|
||||||
|
default:
|
||||||
|
_ = deferred.Warn("", diagnostic.Write)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
written, err := invocation.Streams.Out.Write(output)
|
|
||||||
if err == nil && written != len(output) {
|
|
||||||
err = io.ErrShortWrite
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
outcome := definitionFailure(err, diagnostics)
|
|
||||||
outcome.ReportIncomplete = true
|
|
||||||
outcome.DeliveryError = err
|
|
||||||
return outcome
|
|
||||||
}
|
|
||||||
outcome := commandkit.Success()
|
|
||||||
outcome.Diagnostics = diagnostics
|
|
||||||
return outcome
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func definitionFailure(cause error, diagnostics []commandkit.Diagnostic) commandkit.Outcome {
|
func definitionFailure(message string, explanation commandkit.Explanation, cause error) commandkit.Outcome {
|
||||||
outcome := commandkit.Failure(cause)
|
outcome := commandkit.Failure(message)
|
||||||
outcome.Diagnostics = diagnostics
|
if explanation.Message != "" || len(explanation.Details) > 0 {
|
||||||
|
outcome = outcome.WithExplanation(explanation.Message, explanation.Details...)
|
||||||
|
}
|
||||||
|
if cause != nil {
|
||||||
|
outcome = outcome.WithCause(cause)
|
||||||
|
}
|
||||||
return outcome
|
return outcome
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -701,41 +701,39 @@ func TestCatalogRejectsOperandsAndUnavailableSelectors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCatalogFailuresRetainDiagnosticsAndDeliveryState(t *testing.T) {
|
func TestCatalogFailureReportsWarningAndNativeExplanationSeparately(t *testing.T) {
|
||||||
diagnostic := commandkit.Diagnostic{Severity: commandkit.SeverityWarning, Write: func(writer io.Writer) error {
|
diagnostic := commandkit.MessageDiagnostic(commandkit.SeverityWarning, "catalog warning")
|
||||||
_, err := io.WriteString(writer, "catalog warning\n")
|
|
||||||
return err
|
|
||||||
}}
|
|
||||||
hasCatalogWarning := func(diagnostics []commandkit.Diagnostic) bool {
|
|
||||||
for _, got := range diagnostics {
|
|
||||||
if got.Severity != diagnostic.Severity || got.Write == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var output bytes.Buffer
|
|
||||||
if got.Write(&output) == nil && output.String() == "catalog warning\n" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
providerErr := errors.New("catalog provider failed")
|
providerErr := errors.New("catalog provider failed")
|
||||||
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
|
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
|
||||||
return commandkit.Acquired[[]promptkit.PromptIdentity]{Diagnostics: []commandkit.Diagnostic{diagnostic}}, providerErr
|
return commandkit.Acquired[[]promptkit.PromptIdentity]{
|
||||||
|
Diagnostics: []commandkit.Diagnostic{diagnostic},
|
||||||
|
Explanation: commandkit.Explanation{Details: []commandkit.Detail{func(writer io.Writer) error {
|
||||||
|
_, err := io.WriteString(writer, "PromptKit lookup context\n")
|
||||||
|
return err
|
||||||
|
}}},
|
||||||
|
}, providerErr
|
||||||
}}))
|
}}))
|
||||||
outcome := app.Execute(context.Background(), []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
var output, diagnosticOutput bytes.Buffer
|
||||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, providerErr) || !hasCatalogWarning(outcome.Diagnostics) {
|
outcome := app.Execute(context.Background(), []string{"--verbose", "promptkit", "list", "prompts"}, commandkit.Streams{Out: &output, Err: &diagnosticOutput})
|
||||||
|
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, providerErr) {
|
||||||
t.Fatalf("provider failure outcome = %#v", outcome)
|
t.Fatalf("provider failure outcome = %#v", outcome)
|
||||||
}
|
}
|
||||||
|
warningAt := strings.Index(diagnosticOutput.String(), "catalog warning")
|
||||||
|
explanationAt := strings.Index(diagnosticOutput.String(), "PromptKit lookup context")
|
||||||
|
if warningAt < 0 || explanationAt < 0 || warningAt >= explanationAt || strings.Count(diagnosticOutput.String(), "catalog warning") != 1 {
|
||||||
|
t.Fatalf("warning/native terminal context were not independently ordered once: %q", diagnosticOutput.String())
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
canceled := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
|
canceled := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
|
||||||
cancel()
|
cancel()
|
||||||
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing", Version: testStringPointer("1")}}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil
|
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing", Version: testStringPointer("1")}}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil
|
||||||
}}))
|
}}))
|
||||||
var output bytes.Buffer
|
output.Reset()
|
||||||
outcome = canceled.Execute(ctx, []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: &output, Err: io.Discard})
|
diagnosticOutput.Reset()
|
||||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, context.Canceled) || !hasCatalogWarning(outcome.Diagnostics) || output.Len() != 0 {
|
outcome = canceled.Execute(ctx, []string{"--verbose", "promptkit", "list", "prompts"}, commandkit.Streams{Out: &output, Err: &diagnosticOutput})
|
||||||
t.Fatalf("post-provider cancellation outcome = %#v output=%q", outcome, output.String())
|
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, context.Canceled) || !strings.Contains(diagnosticOutput.String(), "catalog warning") || output.Len() != 0 {
|
||||||
|
t.Fatalf("post-provider cancellation outcome = %#v output=%q err=%q", outcome, output.String(), diagnosticOutput.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
writeErr := errors.New("catalog report writer failed")
|
writeErr := errors.New("catalog report writer failed")
|
||||||
@@ -752,13 +750,96 @@ func TestCatalogFailuresRetainDiagnosticsAndDeliveryState(t *testing.T) {
|
|||||||
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing", Version: testStringPointer("1")}}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil
|
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing", Version: testStringPointer("1")}}, Diagnostics: []commandkit.Diagnostic{diagnostic}}, nil
|
||||||
}}))
|
}}))
|
||||||
outcome := writerApp.Execute(context.Background(), []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: test.writer, Err: io.Discard})
|
outcome := writerApp.Execute(context.Background(), []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: test.writer, Err: io.Discard})
|
||||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.DeliveryError, test.want) || !errors.Is(outcome.Cause, test.want) || !outcome.ReportIncomplete || !hasCatalogWarning(outcome.Diagnostics) {
|
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Presentation.Output.WriteError, test.want) || outcome.Presentation.Output.State != commandkit.DeliveryIncomplete {
|
||||||
t.Fatalf("catalog delivery outcome = %#v", outcome)
|
t.Fatalf("catalog delivery outcome = %#v", outcome)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCatalogAndInspectionRespectSuccessfulReportingModes(t *testing.T) {
|
||||||
|
for _, mode := range []string{"ordinary", "quiet", "verbose", "debug"} {
|
||||||
|
t.Run(mode, func(t *testing.T) {
|
||||||
|
calls := 0
|
||||||
|
suite := Suite[struct{}, struct{}]{
|
||||||
|
ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
|
||||||
|
calls++
|
||||||
|
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing"}}}, nil
|
||||||
|
},
|
||||||
|
InspectPrompt: func(context.Context, PromptRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.PromptInspection], error) {
|
||||||
|
calls++
|
||||||
|
return commandkit.Acquired[*promptkit.PromptInspection]{Value: &promptkit.PromptInspection{PromptID: "briefing"}}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app := newPromptTestApp(t, Command(suite))
|
||||||
|
args := []string{"promptkit", "list", "prompts"}
|
||||||
|
if mode != "ordinary" {
|
||||||
|
args = append([]string{"--" + mode}, args...)
|
||||||
|
}
|
||||||
|
var output, diagnosticOutput bytes.Buffer
|
||||||
|
outcome := app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: &diagnosticOutput})
|
||||||
|
if outcome.Kind != commandkit.OutcomeSuccess || calls != 1 {
|
||||||
|
t.Fatalf("%s listing = %#v after %d calls", mode, outcome, calls)
|
||||||
|
}
|
||||||
|
if mode == "quiet" {
|
||||||
|
if output.Len() != 0 {
|
||||||
|
t.Fatalf("quiet listing wrote result output %q", output.String())
|
||||||
|
}
|
||||||
|
} else if !strings.Contains(output.String(), "briefing") {
|
||||||
|
t.Fatalf("%s listing omitted result: %q", mode, output.String())
|
||||||
|
}
|
||||||
|
if mode == "debug" && !strings.Contains(diagnosticOutput.String(), "DEBUG:") {
|
||||||
|
t.Fatalf("debug listing omitted core debug events: %q", diagnosticOutput.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Reset()
|
||||||
|
diagnosticOutput.Reset()
|
||||||
|
args = []string{"promptkit", "show", "prompt", "briefing"}
|
||||||
|
if mode != "ordinary" {
|
||||||
|
args = append([]string{"--" + mode}, args...)
|
||||||
|
}
|
||||||
|
outcome = app.Execute(context.Background(), args, commandkit.Streams{Out: &output, Err: &diagnosticOutput})
|
||||||
|
if outcome.Kind != commandkit.OutcomeSuccess || calls != 2 {
|
||||||
|
t.Fatalf("%s inspection = %#v after %d callbacks", mode, outcome, calls)
|
||||||
|
}
|
||||||
|
if mode == "quiet" {
|
||||||
|
if output.Len() != 0 {
|
||||||
|
t.Fatalf("quiet inspection wrote result output %q", output.String())
|
||||||
|
}
|
||||||
|
} else if !strings.Contains(output.String(), "Prompt ID: \"briefing\"") {
|
||||||
|
t.Fatalf("%s inspection omitted result: %q", mode, output.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuietInspectionStillFindsReportEncodingFailure(t *testing.T) {
|
||||||
|
called := false
|
||||||
|
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{InspectProfile: func(context.Context, ProfileRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
|
||||||
|
called = true
|
||||||
|
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: &promptkit.ProfileInspection{
|
||||||
|
ProfileID: "broken",
|
||||||
|
EffectiveModelParams: promptkit.ExecutionTarget{ExtraParams: map[string]any{"unsupported": func() {}}},
|
||||||
|
}}, nil
|
||||||
|
}}))
|
||||||
|
result := &countingWriter{}
|
||||||
|
var diagnosticOutput bytes.Buffer
|
||||||
|
outcome := app.Execute(context.Background(), []string{"--quiet", "promptkit", "show", "profile", "broken"}, commandkit.Streams{Out: result, Err: &diagnosticOutput})
|
||||||
|
if !called || result.calls != 0 {
|
||||||
|
t.Fatalf("quiet rendering called=%t destination writes=%d", called, result.calls)
|
||||||
|
}
|
||||||
|
if outcome.Kind != commandkit.OutcomeOperational || outcome.Status != 1 || outcome.Presentation.Output.RenderError == nil {
|
||||||
|
t.Fatalf("quiet serialization failure outcome = %#v diagnostics=%q", outcome, diagnosticOutput.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type countingWriter struct{ calls int }
|
||||||
|
|
||||||
|
func (writer *countingWriter) Write(p []byte) (int, error) {
|
||||||
|
writer.calls++
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestCommandUsesInstalledNamespaceCollisionChecks(t *testing.T) {
|
func TestCommandUsesInstalledNamespaceCollisionChecks(t *testing.T) {
|
||||||
command := Command(Suite[struct{}, struct{}]{InspectPrompt: func(context.Context, PromptRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.PromptInspection], error) {
|
command := Command(Suite[struct{}, struct{}]{InspectPrompt: func(context.Context, PromptRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.PromptInspection], error) {
|
||||||
return commandkit.Acquired[*promptkit.PromptInspection]{}, nil
|
return commandkit.Acquired[*promptkit.PromptInspection]{}, nil
|
||||||
@@ -1055,7 +1136,7 @@ func TestDefinitionFormatPreflightAndReportDeliveryFailure(t *testing.T) {
|
|||||||
|
|
||||||
writeErr := errors.New("report writer failed")
|
writeErr := errors.New("report writer failed")
|
||||||
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "profile", "default"}, commandkit.Streams{Out: partialReportWriter{err: writeErr}, Err: io.Discard})
|
outcome = app.Execute(context.Background(), []string{"promptkit", "show", "profile", "default"}, commandkit.Streams{Out: partialReportWriter{err: writeErr}, Err: io.Discard})
|
||||||
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Cause, writeErr) || outcome.DeliveryError != writeErr || !outcome.ReportIncomplete || calls != 1 {
|
if outcome.Kind != commandkit.OutcomeOperational || !errors.Is(outcome.Presentation.Output.WriteError, writeErr) || outcome.Presentation.Output.State != commandkit.DeliveryIncomplete || calls != 1 {
|
||||||
t.Fatalf("report delivery outcome = %#v calls=%d", outcome, calls)
|
t.Fatalf("report delivery outcome = %#v calls=%d", outcome, calls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user