Separate PromptKit messages from terminal context
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Invocation reporting implementation plan
|
# Invocation reporting implementation plan
|
||||||
|
|
||||||
Status: Stages 1–12 complete. Stage 13 is next.
|
Status: Stages 1–13 complete. Stage 14 is next.
|
||||||
|
|
||||||
## Objective and authority
|
## Objective and authority
|
||||||
|
|
||||||
@@ -496,7 +496,7 @@ old invocation API and is scheduled for migration in Stage 17.
|
|||||||
|
|
||||||
## Stage 13: Migrate PromptKit acquisition adapters
|
## Stage 13: Migrate PromptKit acquisition adapters
|
||||||
|
|
||||||
Status: pending.
|
Status: complete.
|
||||||
|
|
||||||
Primary files: [adapters.go](../../integrations/ckprompt/adapters.go),
|
Primary files: [adapters.go](../../integrations/ckprompt/adapters.go),
|
||||||
[validation.go](../../integrations/ckprompt/validation.go),
|
[validation.go](../../integrations/ckprompt/validation.go),
|
||||||
@@ -521,6 +521,18 @@ Validation: establish correct forwarding, passive data, separated context,
|
|||||||
and supported error identity. Preserve offline fixtures and existing builder/
|
and supported error identity. Preserve offline fixtures and existing builder/
|
||||||
acquisition lifecycle protections. No live provider or credentials are needed.
|
acquisition lifecycle protections. No live provider or credentials are needed.
|
||||||
|
|
||||||
|
PromptKit engine loading now separates independent warning batches from native
|
||||||
|
terminal records and carries both through catalogs, inspection, requirements,
|
||||||
|
ConfigScope comparison, concrete request validation, and smoke acquisition.
|
||||||
|
Native rendering retains scope/side headings, source legends, and diagnostic
|
||||||
|
locations; original errors and reports returned with errors remain available.
|
||||||
|
Offline adapter tests cover mixed record separation, writer propagation,
|
||||||
|
comparison-side context, error identity, and existing request lifetimes. The
|
||||||
|
ckprompt suite cannot execute yet: compiler diagnostics identify legacy
|
||||||
|
reporting calls in `command.go`, `smoke_command.go`, and their tests, which are
|
||||||
|
scheduled for Stages 14 and 15. No compile errors were reported in the migrated
|
||||||
|
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: pending.
|
||||||
|
|||||||
@@ -88,26 +88,33 @@ type Suite[I, S any] struct {
|
|||||||
// CheckInput validates selected inspection, requirements, validation, smoke,
|
// CheckInput validates selected inspection, requirements, validation, smoke,
|
||||||
// or catalog input before state creation.
|
// or catalog input before state creation.
|
||||||
CheckInput func(ckconfig.Selection, I) error
|
CheckInput func(ckconfig.Selection, I) error
|
||||||
// InspectPrompt inspects the selected prompt after input and state preparation.
|
// InspectPrompt inspects the selected prompt after input and state
|
||||||
|
// preparation. Its acquisition carries independent loader diagnostics and
|
||||||
|
// terminal context alongside any native inspection result.
|
||||||
InspectPrompt func(context.Context, PromptRequest, I, S) (commandkit.Acquired[*promptkit.PromptInspection], error)
|
InspectPrompt func(context.Context, PromptRequest, I, S) (commandkit.Acquired[*promptkit.PromptInspection], error)
|
||||||
// InspectProfile inspects the selected profile after input and state preparation.
|
// InspectProfile inspects the selected profile after input and state
|
||||||
|
// preparation. Its acquisition carries independent loader diagnostics and
|
||||||
|
// terminal context alongside any native inspection result.
|
||||||
InspectProfile func(context.Context, ProfileRequest, I, S) (commandkit.Acquired[*promptkit.ProfileInspection], error)
|
InspectProfile func(context.Context, ProfileRequest, I, S) (commandkit.Acquired[*promptkit.ProfileInspection], error)
|
||||||
// InspectRequirements inspects native PromptKit requirements after input and
|
// InspectRequirements inspects native PromptKit requirements after input and
|
||||||
// state preparation. It is optional; a nil callback omits
|
// state preparation. Its acquisition carries independent loader diagnostics
|
||||||
// `promptkit requirements show` without disabling validation or other
|
// and terminal context alongside any native inspection result. It is
|
||||||
// capabilities.
|
// optional; a nil callback omits `promptkit requirements show` without
|
||||||
|
// disabling validation or other capabilities.
|
||||||
InspectRequirements func(context.Context, RequirementsRequest, I, S) (commandkit.Acquired[*promptkit.RequirementInspection], error)
|
InspectRequirements func(context.Context, RequirementsRequest, I, S) (commandkit.Acquired[*promptkit.RequirementInspection], error)
|
||||||
// ValidateRequest validates a concrete PromptKit request after input and
|
// ValidateRequest validates a concrete PromptKit request after input and
|
||||||
// state preparation. It is optional; a nil callback omits
|
// state preparation. It is optional; a nil callback omits
|
||||||
// `promptkit requirements validate` without disabling discovery or other
|
// `promptkit requirements validate` without disabling discovery or other
|
||||||
// capabilities. The acquisition carries diagnostics and an empty value on
|
// capabilities. The acquisition carries independent diagnostics, terminal
|
||||||
// success; PromptKit errors are returned unchanged where supported.
|
// context, and an empty value on success; PromptKit errors are returned
|
||||||
|
// unchanged where supported.
|
||||||
ValidateRequest func(context.Context, ValidationRequest, I, S) (commandkit.Acquired[struct{}], error)
|
ValidateRequest func(context.Context, ValidationRequest, I, S) (commandkit.Acquired[struct{}], error)
|
||||||
// SmokeTest runs one concrete native smoke-test operation after input and
|
// SmokeTest runs one concrete native smoke-test operation after input and
|
||||||
// state preparation. It is optional; nil omits only the smoke-test command.
|
// state preparation. It is optional; nil omits only the smoke-test command.
|
||||||
// The acquisition retains a native report even when the callback also
|
// The acquisition retains a native report, independent diagnostics, and
|
||||||
// returns an error. For `test all`, CommandKit calls the captured callback
|
// terminal context even when the callback also returns an error. For
|
||||||
// sequentially for each selected kind after preparing input and state once.
|
// `test all`, CommandKit calls the captured callback sequentially for each
|
||||||
|
// selected kind after preparing input and state once.
|
||||||
SmokeTest func(context.Context, SmokeRequest, I, S) (commandkit.Acquired[*promptkit.SmokeTestReport], error)
|
SmokeTest func(context.Context, SmokeRequest, I, S) (commandkit.Acquired[*promptkit.SmokeTestReport], error)
|
||||||
// ListPrompts lists prompt identities after input and state preparation.
|
// ListPrompts lists prompt identities after input and state preparation.
|
||||||
// It is optional independently of the other catalogs; nil omits only this
|
// It is optional independently of the other catalogs; nil omits only this
|
||||||
@@ -143,9 +150,10 @@ type Suite[I, S any] struct {
|
|||||||
// selection. A nil builder forwards the seed request unchanged. A builder
|
// selection. A nil builder forwards the seed request unchanged. A builder
|
||||||
// failure prevents engine loading and preserves its supported error identity.
|
// failure prevents engine loading and preserves its supported error identity.
|
||||||
// Native validation errors are returned with supported identity, and
|
// Native validation errors are returned with supported identity, and
|
||||||
// configuration diagnostics from engine loading remain in the acquisition. A
|
// configuration diagnostics and terminal context from engine loading remain
|
||||||
// nil loader is retained as a suite wiring issue and does not install a
|
// separate in the acquisition. A nil loader is retained as a suite wiring
|
||||||
// validation callback. Callback references and objects remain caller-owned.
|
// issue and does not install a validation callback. Callback references and
|
||||||
|
// objects remain caller-owned.
|
||||||
func WithRequestValidation[I, S any](
|
func WithRequestValidation[I, S any](
|
||||||
suite Suite[I, S],
|
suite Suite[I, S],
|
||||||
load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error),
|
load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error),
|
||||||
@@ -175,34 +183,39 @@ func WithRequestValidation[I, S any](
|
|||||||
return commandkit.Acquired[struct{}]{}, fmt.Errorf("PromptKit request validation after building: %w", err)
|
return commandkit.Acquired[struct{}]{}, fmt.Errorf("PromptKit request validation after building: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
if err := promptKitContextError(ctx); err != nil {
|
if err := promptKitContextError(ctx); err != nil {
|
||||||
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit request validation before native validation: %w", err)
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit request validation before native validation: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := engine.ValidateRequest(ctx, concreteRequest); err != nil {
|
if err := engine.ValidateRequest(ctx, concreteRequest); err != nil {
|
||||||
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
if err := promptKitContextError(ctx); err != nil {
|
if err := promptKitContextError(ctx); err != nil {
|
||||||
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit request validation after native validation: %w", err)
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit request validation after native validation: %w", err)
|
||||||
}
|
}
|
||||||
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, nil
|
return commandkit.Acquired[struct{}]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, nil
|
||||||
}
|
}
|
||||||
return suite
|
return suite
|
||||||
}
|
}
|
||||||
|
|
||||||
type engineLoader[I, S any] func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)
|
type engineLoader[I, S any] func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)
|
||||||
|
|
||||||
|
type promptKitAcquisition struct {
|
||||||
|
diagnostics []commandkit.Diagnostic
|
||||||
|
explanation commandkit.Explanation
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigScope adapts PromptKit's captured configuration inspection to the
|
// ConfigScope adapts PromptKit's captured configuration inspection to the
|
||||||
// standard configuration commands, including native comparison when the scope
|
// standard configuration commands, including native comparison when the scope
|
||||||
// declares Diff. The loader is not called during declaration construction or
|
// declares Diff. The loader is not called during declaration construction or
|
||||||
// installation; it is called only when a config operation selects the
|
// installation; it is called only when a config operation selects the
|
||||||
// promptkit scope, with that operation's input and shared state. PromptKit
|
// promptkit scope, with that operation's input and shared state. PromptKit
|
||||||
// diagnostics and supported error identity are returned through ckconfig's
|
// independent diagnostics, terminal context, and supported error identity are
|
||||||
// acquisition and comparison contracts.
|
// returned through ckconfig's acquisition and comparison contracts.
|
||||||
func ConfigScope[I, S any](load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)) ckconfig.Scope[I, S] {
|
func ConfigScope[I, S any](load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)) ckconfig.Scope[I, S] {
|
||||||
scope := ckconfig.Scope[I, S]{
|
scope := ckconfig.Scope[I, S]{
|
||||||
Name: "promptkit",
|
Name: "promptkit",
|
||||||
@@ -214,9 +227,9 @@ func ConfigScope[I, S any](load func(context.Context, ckconfig.Selection, I, S)
|
|||||||
}
|
}
|
||||||
|
|
||||||
scope.Acquire = func(ctx context.Context, request ckconfig.Request, input I, state S) (commandkit.Acquired[ckconfig.View], error) {
|
scope.Acquire = func(ctx context.Context, request ckconfig.Request, input I, state S) (commandkit.Acquired[ckconfig.View], error) {
|
||||||
_, inspection, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
_, inspection, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[ckconfig.View]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[ckconfig.View]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
return commandkit.Acquired[ckconfig.View]{
|
return commandkit.Acquired[ckconfig.View]{
|
||||||
Value: ckconfig.View{
|
Value: ckconfig.View{
|
||||||
@@ -224,7 +237,8 @@ func ConfigScope[I, S any](load func(context.Context, ckconfig.Selection, I, S)
|
|||||||
Sources: inspection,
|
Sources: inspection,
|
||||||
Explanation: inspection,
|
Explanation: inspection,
|
||||||
},
|
},
|
||||||
Diagnostics: diagnostics,
|
Diagnostics: messages.diagnostics,
|
||||||
|
Explanation: messages.explanation,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
scope.Compare = func(ctx context.Context, request ckconfig.ComparisonRequest[I], state S) (commandkit.Acquired[[]configkit.Difference], error) {
|
scope.Compare = func(ctx context.Context, request ckconfig.ComparisonRequest[I], state S) (commandkit.Acquired[[]configkit.Difference], error) {
|
||||||
@@ -232,31 +246,30 @@ func ConfigScope[I, S any](load func(context.Context, ckconfig.Selection, I, S)
|
|||||||
return commandkit.Acquired[[]configkit.Difference]{}, fmt.Errorf("PromptKit scope %q comparison before left load: %w", scope.Name, err)
|
return commandkit.Acquired[[]configkit.Difference]{}, fmt.Errorf("PromptKit scope %q comparison before left load: %w", scope.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, leftInspection, leftDiagnostics, err := loadPromptKitEngine(ctx, request.Left.Selection, request.Left.Input, state, "left", load)
|
_, leftInspection, messages, err := loadPromptKitEngine(ctx, request.Left.Selection, request.Left.Input, state, "left", load)
|
||||||
diagnostics := append([]commandkit.Diagnostic(nil), leftDiagnostics...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit scope %q left comparison load: %w", scope.Name, err)
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q left comparison load: %w", scope.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := promptKitContextError(ctx); err != nil {
|
if err := promptKitContextError(ctx); err != nil {
|
||||||
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit scope %q comparison after left load: %w", scope.Name, err)
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q comparison after left load: %w", scope.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, rightInspection, rightDiagnostics, err := loadPromptKitEngine(ctx, request.Right.Selection, request.Right.Input, state, "right", load)
|
_, rightInspection, rightMessages, err := loadPromptKitEngine(ctx, request.Right.Selection, request.Right.Input, state, "right", load)
|
||||||
diagnostics = append(diagnostics, rightDiagnostics...)
|
messages = appendPromptKitAcquisition(messages, rightMessages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit scope %q right comparison load: %w", scope.Name, err)
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q right comparison load: %w", scope.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := promptKitContextError(ctx); err != nil {
|
if err := promptKitContextError(ctx); err != nil {
|
||||||
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit scope %q comparison before native diff: %w", scope.Name, err)
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q comparison before native diff: %w", scope.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
differences, err := leftInspection.Diff(rightInspection)
|
differences, err := leftInspection.Diff(rightInspection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit scope %q native configuration diff: %w", scope.Name, err)
|
return commandkit.Acquired[[]configkit.Difference]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit scope %q native configuration diff: %w", scope.Name, err)
|
||||||
}
|
}
|
||||||
return commandkit.Acquired[[]configkit.Difference]{Value: differences, Diagnostics: diagnostics}, nil
|
return commandkit.Acquired[[]configkit.Difference]{Value: differences, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, nil
|
||||||
}
|
}
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
@@ -280,8 +293,8 @@ func promptKitContextError(ctx context.Context) error {
|
|||||||
// shared invocation state to the loader before calling PromptKit's public
|
// shared invocation state to the loader before calling PromptKit's public
|
||||||
// listing or inspection method. Applications can narrow, wrap, or replace these
|
// listing or inspection method. Applications can narrow, wrap, or replace these
|
||||||
// callbacks before Command captures them. A nil loader is retained as an
|
// callbacks before Command captures them. A nil loader is retained as an
|
||||||
// adapter issue. Each callback returns loader diagnostics with the native
|
// adapter issue. Each callback returns independent loader diagnostics and
|
||||||
// operation's value and error.
|
// terminal context alongside the native operation's value and error.
|
||||||
func FromEngine[I, S any](load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)) Suite[I, S] {
|
func FromEngine[I, S any](load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)) Suite[I, S] {
|
||||||
if load == nil {
|
if load == nil {
|
||||||
return Suite[I, S]{issues: []error{errors.New("PromptKit engine loader is nil")}}
|
return Suite[I, S]{issues: []error{errors.New("PromptKit engine loader is nil")}}
|
||||||
@@ -289,68 +302,68 @@ func FromEngine[I, S any](load func(context.Context, ckconfig.Selection, I, S) (
|
|||||||
|
|
||||||
return Suite[I, S]{
|
return Suite[I, S]{
|
||||||
ListPrompts: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
|
ListPrompts: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[[]promptkit.PromptIdentity]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]promptkit.PromptIdentity]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
prompts, err := engine.ListPrompts(ctx, request.Filter)
|
prompts, err := engine.ListPrompts(ctx, request.Filter)
|
||||||
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: prompts, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: prompts, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
},
|
},
|
||||||
ListProfiles: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
ListProfiles: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[[]string]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]string]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
profiles, err := engine.ListProfiles(ctx, request.Filter)
|
profiles, err := engine.ListProfiles(ctx, request.Filter)
|
||||||
return commandkit.Acquired[[]string]{Value: profiles, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]string]{Value: profiles, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
},
|
},
|
||||||
ListBackends: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
ListBackends: func(ctx context.Context, request CatalogRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[[]string]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]string]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
backends, err := engine.ListBackends(ctx, request.Filter)
|
backends, err := engine.ListBackends(ctx, request.Filter)
|
||||||
return commandkit.Acquired[[]string]{Value: backends, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]string]{Value: backends, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
},
|
},
|
||||||
ListNamespaces: func(ctx context.Context, request NamespaceRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
ListNamespaces: func(ctx context.Context, request NamespaceRequest, input I, state S) (commandkit.Acquired[[]string], error) {
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[[]string]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]string]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
namespaces, err := engine.ListNamespaces(ctx, request.Kinds...)
|
namespaces, err := engine.ListNamespaces(ctx, request.Kinds...)
|
||||||
return commandkit.Acquired[[]string]{Value: namespaces, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]string]{Value: namespaces, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
},
|
},
|
||||||
ListCollections: func(ctx context.Context, selection ckconfig.Selection, input I, state S) (commandkit.Acquired[[]promptkit.CollectionInfo], error) {
|
ListCollections: func(ctx context.Context, selection ckconfig.Selection, input I, state S) (commandkit.Acquired[[]promptkit.CollectionInfo], error) {
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[[]promptkit.CollectionInfo]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]promptkit.CollectionInfo]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
collections, err := engine.ListCollections(ctx)
|
collections, err := engine.ListCollections(ctx)
|
||||||
return commandkit.Acquired[[]promptkit.CollectionInfo]{Value: collections, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[[]promptkit.CollectionInfo]{Value: collections, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
},
|
},
|
||||||
InspectPrompt: func(ctx context.Context, request PromptRequest, input I, state S) (commandkit.Acquired[*promptkit.PromptInspection], error) {
|
InspectPrompt: func(ctx context.Context, request PromptRequest, input I, state S) (commandkit.Acquired[*promptkit.PromptInspection], error) {
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[*promptkit.PromptInspection]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[*promptkit.PromptInspection]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
inspection, err := engine.InspectPrompt(ctx, request.ID)
|
inspection, err := engine.InspectPrompt(ctx, request.ID)
|
||||||
return commandkit.Acquired[*promptkit.PromptInspection]{Value: inspection, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[*promptkit.PromptInspection]{Value: inspection, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
},
|
},
|
||||||
InspectProfile: func(ctx context.Context, request ProfileRequest, input I, state S) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
|
InspectProfile: func(ctx context.Context, request ProfileRequest, input I, state S) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[*promptkit.ProfileInspection]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[*promptkit.ProfileInspection]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
inspection, err := engine.InspectProfile(ctx, request.ID)
|
inspection, err := engine.InspectProfile(ctx, request.ID)
|
||||||
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: inspection, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: inspection, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
},
|
},
|
||||||
InspectRequirements: func(ctx context.Context, request RequirementsRequest, input I, state S) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
InspectRequirements: func(ctx context.Context, request RequirementsRequest, input I, state S) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[*promptkit.RequirementInspection]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
inspection, err := engine.InspectRequirements(ctx, request.Inspection)
|
inspection, err := engine.InspectRequirements(ctx, request.Inspection)
|
||||||
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: inspection, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[*promptkit.RequirementInspection]{Value: inspection, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,41 +375,44 @@ func loadPromptKitEngine[I, S any](
|
|||||||
state S,
|
state S,
|
||||||
side string,
|
side string,
|
||||||
load engineLoader[I, S],
|
load engineLoader[I, S],
|
||||||
) (*promptkit.Engine, promptkit.ConfigurationInspection, []commandkit.Diagnostic, error) {
|
) (*promptkit.Engine, promptkit.ConfigurationInspection, promptKitAcquisition, error) {
|
||||||
engine, err := load(ctx, selection, input, state)
|
engine, err := load(ctx, selection, input, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var loadErr *configkit.LoadError
|
var loadErr *configkit.LoadError
|
||||||
if errors.As(err, &loadErr) {
|
if errors.As(err, &loadErr) {
|
||||||
return nil, promptkit.ConfigurationInspection{}, promptKitDiagnostics("promptkit", side, loadErr.Sources(), loadErr.Diagnostics()), err
|
messages := promptKitDiagnostics("promptkit", side, loadErr.Sources(), loadErr.Diagnostics())
|
||||||
|
return nil, promptkit.ConfigurationInspection{}, messages, err
|
||||||
}
|
}
|
||||||
return nil, promptkit.ConfigurationInspection{}, nil, err
|
return nil, promptkit.ConfigurationInspection{}, promptKitAcquisition{}, err
|
||||||
}
|
}
|
||||||
if engine == nil {
|
if engine == nil {
|
||||||
return nil, promptkit.ConfigurationInspection{}, nil, errors.New("PromptKit configuration loader returned a nil engine")
|
return nil, promptkit.ConfigurationInspection{}, promptKitAcquisition{}, errors.New("PromptKit configuration loader returned a nil engine")
|
||||||
}
|
}
|
||||||
inspection, err := engine.InspectConfiguration()
|
inspection, err := engine.InspectConfiguration()
|
||||||
|
messages := promptKitDiagnostics("promptkit", side, inspection.Sources(), inspection.Diagnostics())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, promptkit.ConfigurationInspection{}, nil, err
|
return nil, inspection, messages, err
|
||||||
}
|
}
|
||||||
diagnostics := promptKitDiagnostics("promptkit", side, inspection.Sources(), inspection.Diagnostics())
|
|
||||||
if ctx != nil {
|
if ctx != nil {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, promptkit.ConfigurationInspection{}, diagnostics, err
|
return nil, promptkit.ConfigurationInspection{}, messages, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return engine, inspection, diagnostics, nil
|
return engine, inspection, messages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func promptKitDiagnostics(scope, side string, sources []configkit.SourceInfo, records []configkit.Diagnostic) []commandkit.Diagnostic {
|
func appendPromptKitAcquisition(left, right promptKitAcquisition) promptKitAcquisition {
|
||||||
if len(records) == 0 {
|
left.diagnostics = append(left.diagnostics, right.diagnostics...)
|
||||||
return nil
|
left.explanation.Details = append(left.explanation.Details, right.explanation.Details...)
|
||||||
|
if left.explanation.Message == "" {
|
||||||
|
left.explanation.Message = right.explanation.Message
|
||||||
}
|
}
|
||||||
severity := commandkit.SeverityWarning
|
return left
|
||||||
for _, record := range records {
|
}
|
||||||
if record.Severity == configkit.SeverityError {
|
|
||||||
severity = commandkit.SeverityError
|
func promptKitDiagnostics(scope, side string, sources []configkit.SourceInfo, records []configkit.Diagnostic) promptKitAcquisition {
|
||||||
break
|
if len(records) == 0 {
|
||||||
}
|
return promptKitAcquisition{}
|
||||||
}
|
}
|
||||||
// Snapshot provider-owned slices before deferring their report to invocation.
|
// Snapshot provider-owned slices before deferring their report to invocation.
|
||||||
sources = append([]configkit.SourceInfo(nil), sources...)
|
sources = append([]configkit.SourceInfo(nil), sources...)
|
||||||
@@ -408,27 +424,48 @@ func promptKitDiagnostics(scope, side string, sources []configkit.SourceInfo, re
|
|||||||
}
|
}
|
||||||
records[index].Related = append([]configkit.Location(nil), records[index].Related...)
|
records[index].Related = append([]configkit.Location(nil), records[index].Related...)
|
||||||
}
|
}
|
||||||
return []commandkit.Diagnostic{{
|
var warnings, failures []configkit.Diagnostic
|
||||||
Severity: severity,
|
for _, record := range records {
|
||||||
Write: func(writer io.Writer) error {
|
if record.Severity == configkit.SeverityError {
|
||||||
heading := "Configuration scope " + strconv.Quote(scope)
|
failures = append(failures, record)
|
||||||
if side != "" {
|
} else {
|
||||||
heading += " (" + side + ")"
|
warnings = append(warnings, record)
|
||||||
}
|
}
|
||||||
if _, err := fmt.Fprintf(writer, "%s:\n", heading); err != nil {
|
}
|
||||||
|
messages := promptKitAcquisition{}
|
||||||
|
if len(warnings) > 0 {
|
||||||
|
messages.diagnostics = []commandkit.Diagnostic{{
|
||||||
|
Severity: commandkit.SeverityWarning,
|
||||||
|
Write: func(writer io.Writer) error {
|
||||||
|
return writePromptKitDiagnostics(writer, scope, side, sources, warnings)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
if len(failures) > 0 {
|
||||||
|
messages.explanation = commandkit.Explanation{Details: []commandkit.Detail{func(writer io.Writer) error {
|
||||||
|
return writePromptKitDiagnostics(writer, scope, side, sources, failures)
|
||||||
|
}}}
|
||||||
|
}
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePromptKitDiagnostics(writer io.Writer, scope, side string, sources []configkit.SourceInfo, records []configkit.Diagnostic) error {
|
||||||
|
heading := "Configuration scope " + strconv.Quote(scope)
|
||||||
|
if side != "" {
|
||||||
|
heading += " (" + side + ")"
|
||||||
|
}
|
||||||
|
if _, err := fmt.Fprintf(writer, "%s:\n", heading); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(sources) > 0 {
|
||||||
|
if _, err := io.WriteString(writer, "Source IDs:\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, source := range sources {
|
||||||
|
if _, err := fmt.Fprintf(writer, " %s: label=%s, path=%s\n", strconv.Quote(source.ID), strconv.Quote(source.Label), strconv.Quote(source.Path)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(sources) > 0 {
|
}
|
||||||
if _, err := io.WriteString(writer, "Source IDs:\n"); err != nil {
|
}
|
||||||
return err
|
return configkit.WriteDiagnostics(writer, records)
|
||||||
}
|
|
||||||
for _, source := range sources {
|
|
||||||
if _, err := fmt.Fprintf(writer, " %s: label=%s, path=%s\n", strconv.Quote(source.ID), strconv.Quote(source.Label), strconv.Quote(source.Path)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configkit.WriteDiagnostics(writer, records)
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ func TestWithSmokeTestingRetainsNilLoaderIssueAndChecksCancellationBeforeBuild(t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithSmokeTestingRetainsLoaderDiagnosticsAndErrorIdentity(t *testing.T) {
|
func TestWithSmokeTestingRetainsLoaderExplanationAndErrorIdentity(t *testing.T) {
|
||||||
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid smoke configuration", []byte("broken: [")))
|
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid smoke configuration", []byte("broken: [")))
|
||||||
if loadErr == nil {
|
if loadErr == nil {
|
||||||
t.Fatal("invalid configuration unexpectedly loaded")
|
t.Fatal("invalid configuration unexpectedly loaded")
|
||||||
@@ -249,7 +249,7 @@ func TestWithSmokeTestingRetainsLoaderDiagnosticsAndErrorIdentity(t *testing.T)
|
|||||||
}, nil)
|
}, nil)
|
||||||
acquired, err := suite.SmokeTest(context.Background(), SmokeRequest{}, 0, 0)
|
acquired, err := suite.SmokeTest(context.Background(), SmokeRequest{}, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.Is(err, loadErr) || !errors.As(err, &retained) || retained != loadErr || acquired.Value != nil || len(acquired.Diagnostics) == 0 {
|
if !errors.Is(err, loadErr) || !errors.As(err, &retained) || retained != loadErr || acquired.Value != nil || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Fatalf("smoke load failure = (%#v, %v)", acquired, err)
|
t.Fatalf("smoke load failure = (%#v, %v)", acquired, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -394,7 +394,7 @@ func TestConfigScopeCompareStopsAfterLeftLoadFailure(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigScopeCompareRetainsRightLoadDiagnosticsAndErrorIdentity(t *testing.T) {
|
func TestConfigScopeCompareRetainsRightLoadExplanationAndErrorIdentity(t *testing.T) {
|
||||||
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid right prompt config", []byte("broken: [")))
|
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid right prompt config", []byte("broken: [")))
|
||||||
if loadErr == nil {
|
if loadErr == nil {
|
||||||
t.Fatal("invalid right configuration unexpectedly loaded")
|
t.Fatal("invalid right configuration unexpectedly loaded")
|
||||||
@@ -415,12 +415,12 @@ func TestConfigScopeCompareRetainsRightLoadDiagnosticsAndErrorIdentity(t *testin
|
|||||||
|
|
||||||
acquired, err := scope.Compare(context.Background(), request, struct{}{})
|
acquired, err := scope.Compare(context.Background(), request, struct{}{})
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.Is(err, loadErr) || !errors.As(err, &retained) || retained != loadErr || len(calls) != 2 || len(acquired.Diagnostics) == 0 {
|
if !errors.Is(err, loadErr) || !errors.As(err, &retained) || retained != loadErr || len(calls) != 2 || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Fatalf("right failure = (%#v, %v), calls=%#v", acquired, err, calls)
|
t.Fatalf("right failure = (%#v, %v), calls=%#v", acquired, err, calls)
|
||||||
}
|
}
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
if writeErr := acquired.Diagnostics[0].Write(&output); writeErr != nil || !strings.Contains(output.String(), `Configuration scope "promptkit" (right):`) {
|
if writeErr := acquired.Explanation.Details[0](&output); writeErr != nil || !strings.Contains(output.String(), `Configuration scope "promptkit" (right):`) {
|
||||||
t.Fatalf("right diagnostic = %q, %v", output.String(), writeErr)
|
t.Fatalf("right explanation = %q, %v", output.String(), writeErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,7 +534,7 @@ func TestFromEngineInspectsRequirementsWithNativeRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromEngineRequirementsRetainLoadDiagnosticsAndSelectionErrors(t *testing.T) {
|
func TestFromEngineRequirementsRetainLoadExplanationAndSelectionErrors(t *testing.T) {
|
||||||
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid requirements configuration", []byte("broken: [")))
|
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid requirements configuration", []byte("broken: [")))
|
||||||
if loadErr == nil {
|
if loadErr == nil {
|
||||||
t.Fatal("invalid configuration unexpectedly loaded")
|
t.Fatal("invalid configuration unexpectedly loaded")
|
||||||
@@ -544,11 +544,8 @@ func TestFromEngineRequirementsRetainLoadDiagnosticsAndSelectionErrors(t *testin
|
|||||||
})
|
})
|
||||||
acquired, err := loadFailure.InspectRequirements(context.Background(), RequirementsRequest{}, 0, 0)
|
acquired, err := loadFailure.InspectRequirements(context.Background(), RequirementsRequest{}, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 || acquired.Value != nil {
|
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 || acquired.Value != nil {
|
||||||
t.Fatalf("requirements load failure = (%#v, %v), want original ConfigKit error and diagnostics", acquired, err)
|
t.Fatalf("requirements load failure = (%#v, %v), want original ConfigKit error and terminal context", acquired, err)
|
||||||
}
|
|
||||||
if acquired.Diagnostics[0].Severity != commandkit.SeverityError {
|
|
||||||
t.Fatalf("load diagnostics = %#v, want error severity", acquired.Diagnostics)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nilEngine := FromEngine[int, int](func(context.Context, ckconfig.Selection, int, int) (*promptkit.Engine, error) {
|
nilEngine := FromEngine[int, int](func(context.Context, ckconfig.Selection, int, int) (*promptkit.Engine, error) {
|
||||||
@@ -651,7 +648,7 @@ func TestFromEngineListsNativeCatalogsWithInvocationValues(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromEngineCatalogsRetainLoadDiagnosticsAndErrorIdentity(t *testing.T) {
|
func TestFromEngineCatalogsRetainLoadExplanationAndErrorIdentity(t *testing.T) {
|
||||||
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid catalog configuration", []byte("broken: [")))
|
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid catalog configuration", []byte("broken: [")))
|
||||||
if loadErr == nil {
|
if loadErr == nil {
|
||||||
t.Fatal("invalid configuration unexpectedly loaded")
|
t.Fatal("invalid configuration unexpectedly loaded")
|
||||||
@@ -666,42 +663,42 @@ func TestFromEngineCatalogsRetainLoadDiagnosticsAndErrorIdentity(t *testing.T) {
|
|||||||
{name: "prompts", call: func() (int, error) {
|
{name: "prompts", call: func() (int, error) {
|
||||||
acquired, err := suite.ListPrompts(context.Background(), CatalogRequest{}, 0, 0)
|
acquired, err := suite.ListPrompts(context.Background(), CatalogRequest{}, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
|
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Errorf("prompt load failure = (%#v, %v)", acquired, err)
|
t.Errorf("prompt load failure = (%#v, %v)", acquired, err)
|
||||||
}
|
}
|
||||||
return len(acquired.Diagnostics), err
|
return len(acquired.Diagnostics) + len(acquired.Explanation.Details), err
|
||||||
}},
|
}},
|
||||||
{name: "profiles", call: func() (int, error) {
|
{name: "profiles", call: func() (int, error) {
|
||||||
acquired, err := suite.ListProfiles(context.Background(), CatalogRequest{}, 0, 0)
|
acquired, err := suite.ListProfiles(context.Background(), CatalogRequest{}, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
|
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Errorf("profile load failure = (%#v, %v)", acquired, err)
|
t.Errorf("profile load failure = (%#v, %v)", acquired, err)
|
||||||
}
|
}
|
||||||
return len(acquired.Diagnostics), err
|
return len(acquired.Diagnostics) + len(acquired.Explanation.Details), err
|
||||||
}},
|
}},
|
||||||
{name: "backends", call: func() (int, error) {
|
{name: "backends", call: func() (int, error) {
|
||||||
acquired, err := suite.ListBackends(context.Background(), CatalogRequest{}, 0, 0)
|
acquired, err := suite.ListBackends(context.Background(), CatalogRequest{}, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
|
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Errorf("backend load failure = (%#v, %v)", acquired, err)
|
t.Errorf("backend load failure = (%#v, %v)", acquired, err)
|
||||||
}
|
}
|
||||||
return len(acquired.Diagnostics), err
|
return len(acquired.Diagnostics) + len(acquired.Explanation.Details), err
|
||||||
}},
|
}},
|
||||||
{name: "namespaces", call: func() (int, error) {
|
{name: "namespaces", call: func() (int, error) {
|
||||||
acquired, err := suite.ListNamespaces(context.Background(), NamespaceRequest{}, 0, 0)
|
acquired, err := suite.ListNamespaces(context.Background(), NamespaceRequest{}, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
|
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Errorf("namespace load failure = (%#v, %v)", acquired, err)
|
t.Errorf("namespace load failure = (%#v, %v)", acquired, err)
|
||||||
}
|
}
|
||||||
return len(acquired.Diagnostics), err
|
return len(acquired.Diagnostics) + len(acquired.Explanation.Details), err
|
||||||
}},
|
}},
|
||||||
{name: "collections", call: func() (int, error) {
|
{name: "collections", call: func() (int, error) {
|
||||||
acquired, err := suite.ListCollections(context.Background(), ckconfig.Selection{}, 0, 0)
|
acquired, err := suite.ListCollections(context.Background(), ckconfig.Selection{}, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
|
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Errorf("collection load failure = (%#v, %v)", acquired, err)
|
t.Errorf("collection load failure = (%#v, %v)", acquired, err)
|
||||||
}
|
}
|
||||||
return len(acquired.Diagnostics), err
|
return len(acquired.Diagnostics) + len(acquired.Explanation.Details), err
|
||||||
}},
|
}},
|
||||||
} {
|
} {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
@@ -782,7 +779,7 @@ func TestAdapterAcquisitionPreservesLoadAndContextErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFromEngineRetainsConfigKitLoadDiagnostics(t *testing.T) {
|
func TestFromEngineRetainsConfigKitLoadExplanation(t *testing.T) {
|
||||||
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid PromptKit config", []byte("broken: [")))
|
_, loadErr := configkit.Load[map[string]any](configkit.Bytes("invalid PromptKit config", []byte("broken: [")))
|
||||||
if loadErr == nil {
|
if loadErr == nil {
|
||||||
t.Fatal("invalid configuration unexpectedly loaded")
|
t.Fatal("invalid configuration unexpectedly loaded")
|
||||||
@@ -793,11 +790,8 @@ func TestFromEngineRetainsConfigKitLoadDiagnostics(t *testing.T) {
|
|||||||
|
|
||||||
acquired, err := suite.InspectProfile(context.Background(), ProfileRequest{ID: "default"}, 0, 0)
|
acquired, err := suite.InspectProfile(context.Background(), ProfileRequest{ID: "default"}, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
|
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Fatalf("load failure = (%#v, %v), want original ConfigKit error and diagnostics", acquired, err)
|
t.Fatalf("load failure = (%#v, %v), want original ConfigKit error and terminal context", acquired, err)
|
||||||
}
|
|
||||||
if acquired.Diagnostics[0].Severity != commandkit.SeverityError {
|
|
||||||
t.Fatalf("load diagnostics = %#v, want error severity", acquired.Diagnostics)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,25 +817,55 @@ func TestPromptKitDiagnosticsRemainDeferredAndPropagateWriterErrors(t *testing.T
|
|||||||
Severity: configkit.SeverityWarning,
|
Severity: configkit.SeverityWarning,
|
||||||
Message: "safe warning",
|
Message: "safe warning",
|
||||||
}}
|
}}
|
||||||
diagnostics := promptKitDiagnostics("promptkit", "", sources, records)
|
messages := promptKitDiagnostics("promptkit", "", sources, records)
|
||||||
if len(diagnostics) != 1 || diagnostics[0].Severity != commandkit.SeverityWarning {
|
if len(messages.diagnostics) != 1 || messages.diagnostics[0].Severity != commandkit.SeverityWarning || len(messages.explanation.Details) != 0 {
|
||||||
t.Fatalf("diagnostics = %#v", diagnostics)
|
t.Fatalf("diagnostic messages = %#v", messages)
|
||||||
}
|
}
|
||||||
if err := diagnostics[0].Write(failingDiagnosticWriter{err: writeErr}); !errors.Is(err, writeErr) {
|
if err := messages.diagnostics[0].Write(failingDiagnosticWriter{err: writeErr}); !errors.Is(err, writeErr) {
|
||||||
t.Fatalf("diagnostic write error = %v", err)
|
t.Fatalf("diagnostic write error = %v", err)
|
||||||
}
|
}
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
if err := diagnostics[0].Write(&output); err != nil || !strings.Contains(output.String(), `Configuration scope "promptkit":`) || !strings.Contains(output.String(), "safe warning") || !strings.Contains(output.String(), `"source"`) {
|
if err := messages.diagnostics[0].Write(&output); err != nil || !strings.Contains(output.String(), `Configuration scope "promptkit":`) || !strings.Contains(output.String(), "safe warning") || !strings.Contains(output.String(), `"source"`) {
|
||||||
t.Fatalf("diagnostic text = %q, %v", output.String(), err)
|
t.Fatalf("diagnostic text = %q, %v", output.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sideDiagnostics := promptKitDiagnostics("promptkit", "left", sources, records)
|
sideDiagnostics := promptKitDiagnostics("promptkit", "left", sources, records)
|
||||||
output.Reset()
|
output.Reset()
|
||||||
if err := sideDiagnostics[0].Write(&output); err != nil || !strings.Contains(output.String(), `Configuration scope "promptkit" (left):`) {
|
if err := sideDiagnostics.diagnostics[0].Write(&output); err != nil || !strings.Contains(output.String(), `Configuration scope "promptkit" (left):`) {
|
||||||
t.Fatalf("comparison-side diagnostic text = %q, %v", output.String(), err)
|
t.Fatalf("comparison-side diagnostic text = %q, %v", output.String(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPromptKitDiagnosticsSeparateIndependentWarningsAndTerminalRecords(t *testing.T) {
|
||||||
|
sources := []configkit.SourceInfo{{ID: "source", Label: "safe label", Path: "config.yml"}}
|
||||||
|
primary := configkit.Location{SourceID: "source", Line: 3, Column: 2}
|
||||||
|
related := configkit.Location{SourceID: "base.yml", Line: 7, Column: 4}
|
||||||
|
messages := promptKitDiagnostics("promptkit", "right", sources, []configkit.Diagnostic{
|
||||||
|
{Severity: configkit.SeverityWarning, Message: "independent warning", Primary: &primary, Related: []configkit.Location{related}},
|
||||||
|
{Severity: configkit.SeverityError, Message: "terminal configuration error", Primary: &related, Related: []configkit.Location{primary}},
|
||||||
|
})
|
||||||
|
if len(messages.diagnostics) != 1 || len(messages.explanation.Details) != 1 {
|
||||||
|
t.Fatalf("mixed acquisition messages = %#v", messages)
|
||||||
|
}
|
||||||
|
writeErr := errors.New("terminal explanation writer failed")
|
||||||
|
if err := messages.explanation.Details[0](failingDiagnosticWriter{err: writeErr}); !errors.Is(err, writeErr) {
|
||||||
|
t.Fatalf("terminal explanation write error = %v", err)
|
||||||
|
}
|
||||||
|
var warning, terminal bytes.Buffer
|
||||||
|
if err := messages.diagnostics[0].Write(&warning); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := messages.explanation.Details[0](&terminal); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := warning.String(); !strings.Contains(got, "independent warning") || !strings.Contains(got, `Configuration scope "promptkit" (right):`) || !strings.Contains(got, `"source": label="safe label", path="config.yml"`) || !strings.Contains(got, "source:3:2") || !strings.Contains(got, "related: base.yml:7:4") || strings.Contains(got, "terminal configuration error") {
|
||||||
|
t.Fatalf("independent warning report = %q", got)
|
||||||
|
}
|
||||||
|
if got := terminal.String(); !strings.Contains(got, "terminal configuration error") || !strings.Contains(got, `Configuration scope "promptkit" (right):`) || !strings.Contains(got, `"source": label="safe label", path="config.yml"`) || !strings.Contains(got, "base.yml:7:4") || !strings.Contains(got, "related: source:3:2") || strings.Contains(got, "independent warning") {
|
||||||
|
t.Fatalf("terminal explanation report = %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type failingDiagnosticWriter struct{ err error }
|
type failingDiagnosticWriter struct{ err error }
|
||||||
|
|
||||||
func (writer failingDiagnosticWriter) Write([]byte) (int, error) { return 0, writer.err }
|
func (writer failingDiagnosticWriter) Write([]byte) (int, error) { return 0, writer.err }
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ type SmokeRequest struct {
|
|||||||
// invocation context. Its complete returned request is passed to PromptKit;
|
// invocation context. Its complete returned request is passed to PromptKit;
|
||||||
// without a builder, the seed is forwarded unchanged. Reports and errors from
|
// without a builder, the seed is forwarded unchanged. Reports and errors from
|
||||||
// PromptKit are both retained. A nil loader is kept as a suite wiring issue and
|
// PromptKit are both retained. A nil loader is kept as a suite wiring issue and
|
||||||
// does not install a smoke-test callback. The callback also retains engine
|
// does not install a smoke-test callback. The callback also retains independent
|
||||||
// loading diagnostics with the report and error.
|
// engine diagnostics and terminal context with the report and error.
|
||||||
func WithSmokeTesting[I, S any](
|
func WithSmokeTesting[I, S any](
|
||||||
suite Suite[I, S],
|
suite Suite[I, S],
|
||||||
load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error),
|
load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error),
|
||||||
@@ -56,16 +56,16 @@ func WithSmokeTesting[I, S any](
|
|||||||
return commandkit.Acquired[*promptkit.SmokeTestReport]{}, fmt.Errorf("PromptKit smoke test after building: %w", err)
|
return commandkit.Acquired[*promptkit.SmokeTestReport]{}, fmt.Errorf("PromptKit smoke test after building: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
engine, _, messages, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return commandkit.Acquired[*promptkit.SmokeTestReport]{Diagnostics: diagnostics}, err
|
return commandkit.Acquired[*promptkit.SmokeTestReport]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
if err := promptKitContextError(ctx); err != nil {
|
if err := promptKitContextError(ctx); err != nil {
|
||||||
return commandkit.Acquired[*promptkit.SmokeTestReport]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit smoke test before native operation: %w", err)
|
return commandkit.Acquired[*promptkit.SmokeTestReport]{Diagnostics: messages.diagnostics, Explanation: messages.explanation}, fmt.Errorf("PromptKit smoke test before native operation: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
report, err := engine.SmokeTest(ctx, concreteRequest)
|
report, err := engine.SmokeTest(ctx, concreteRequest)
|
||||||
return commandkit.Acquired[*promptkit.SmokeTestReport]{Value: report, Diagnostics: diagnostics}, err
|
return commandkit.Acquired[*promptkit.SmokeTestReport]{Value: report, Diagnostics: messages.diagnostics, Explanation: messages.explanation}, err
|
||||||
}
|
}
|
||||||
return suite
|
return suite
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ func TestWithRequestValidationBuilderOutputReachesNativeValidation(t *testing.T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWithRequestValidationRetainsNilEngineAndLoadDiagnostics(t *testing.T) {
|
func TestWithRequestValidationRetainsNilEngineAndLoadExplanation(t *testing.T) {
|
||||||
request := ValidationRequest{Request: promptkit.RunRequest{PromptID: "empty", ProfileID: "default"}}
|
request := ValidationRequest{Request: promptkit.RunRequest{PromptID: "empty", ProfileID: "default"}}
|
||||||
nilEngineSuite := WithRequestValidation(Suite[struct{}, struct{}]{}, func(context.Context, ckconfig.Selection, struct{}, struct{}) (*promptkit.Engine, error) {
|
nilEngineSuite := WithRequestValidation(Suite[struct{}, struct{}]{}, func(context.Context, ckconfig.Selection, struct{}, struct{}) (*promptkit.Engine, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -232,8 +232,8 @@ func TestWithRequestValidationRetainsNilEngineAndLoadDiagnostics(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
acquired, err = loadFailure.ValidateRequest(context.Background(), request, struct{}{}, struct{}{})
|
acquired, err = loadFailure.ValidateRequest(context.Background(), request, struct{}{}, struct{}{})
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.Is(err, loadErr) || !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
|
if !errors.Is(err, loadErr) || !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 {
|
||||||
t.Fatalf("load failure = (%#v, %v), want original ConfigKit error and diagnostics", acquired, err)
|
t.Fatalf("load failure = (%#v, %v), want original ConfigKit error and terminal context", acquired, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ func TestWithRequestValidationCancellationBoundaries(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
acquired, err := suite.ValidateRequest(ctx, request, 0, 0)
|
acquired, err := suite.ValidateRequest(ctx, request, 0, 0)
|
||||||
var retained *configkit.LoadError
|
var retained *configkit.LoadError
|
||||||
if !errors.Is(err, loadErr) || !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 || loaderCalls != 1 {
|
if !errors.Is(err, loadErr) || !errors.As(err, &retained) || retained != loadErr || len(acquired.Explanation.Details) == 0 || loaderCalls != 1 {
|
||||||
t.Fatalf("cancelled load = (%#v, %v), loader calls=%d", acquired, err, loaderCalls)
|
t.Fatalf("cancelled load = (%#v, %v), loader calls=%d", acquired, err, loaderCalls)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user