Migrate PromptKit inspection reports to the reporter

This commit is contained in:
2026-10-26 03:30:19 +00:00
parent 37dfea5b25
commit 7fc89768ef
3 changed files with 204 additions and 102 deletions

View File

@@ -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.

View File

@@ -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
} }

View File

@@ -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)
} }
} }