Extend native catalog adapters

This commit is contained in:
2026-10-17 18:44:10 +00:00
parent c1fa18629d
commit dc4261a1ca
5 changed files with 182 additions and 49 deletions

View File

@@ -1,6 +1,6 @@
# Dependency upgrades, discovery, and smoke testing implementation plan # Dependency upgrades, discovery, and smoke testing implementation plan
Status: Stages 1–5 complete; Stage 6 is next. Status: Stages 1–6 complete; Stage 7 is next.
## Authority and baseline ## Authority and baseline
@@ -381,6 +381,15 @@ operator syntax delivered in Stage 7.
**Complete when:** all five catalogs are available through narrow native adapters **Complete when:** all five catalogs are available through narrow native adapters
and public callback shapes are coherent. and public callback shapes are coherent.
**Completed:** 2026-10-17. Added `CatalogRequest` and `NamespaceRequest`,
updated the prompt/profile callbacks, and exposed backend, namespace, and
collection callbacks. `FromEngine` forwards selections and native filters or
kinds to all five PromptKit catalog methods, preserving returned values,
diagnostics, and errors. Existing catalog callers now send the default request.
Focused adapter tests compare each callback with its native method and cover
invocation values, loader diagnostics/error identity, and cancellation. The
focused adapter/catalog tests and `GOTOOLCHAIN=local go build ./...` passed.
## Stage 7: Add catalog command bindings and YAML output ## Stage 7: Add catalog command bindings and YAML output
**Primary files:** ckprompt catalog command construction, input preparation, **Primary files:** ckprompt catalog command construction, input preparation,

View File

@@ -29,6 +29,28 @@ type ProfileRequest struct {
ID string ID string
} }
// CatalogRequest selects the configuration and native namespace filter for a
// prompt, profile, or backend catalog operation. A zero Filter preserves
// PromptKit's native default selection.
type CatalogRequest struct {
// Selection carries the configuration and overlay supplied to the command.
Selection ckconfig.Selection
// Filter is forwarded unchanged to each filterable native catalog. Its zero
// value preserves PromptKit's default namespace selection.
Filter promptkit.NamespaceFilter
}
// NamespaceRequest selects the configuration and native resource kinds for a
// namespace catalog operation. An empty Kinds slice preserves PromptKit's
// native default selection.
type NamespaceRequest struct {
// Selection carries the configuration and overlay supplied to the command.
Selection ckconfig.Selection
// Kinds is forwarded unchanged to the native namespace catalog. An empty
// slice preserves PromptKit's default kind selection.
Kinds []promptkit.ResourceKind
}
// RequirementsRequest selects a prompt context for native requirements // RequirementsRequest selects a prompt context for native requirements
// inspection. // inspection.
type RequirementsRequest struct { type RequirementsRequest struct {
@@ -81,11 +103,25 @@ type Suite[I, S any] struct {
// success; PromptKit errors are returned unchanged where supported. // 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)
// ListPrompts lists prompt identities after input and state preparation. // ListPrompts lists prompt identities after input and state preparation.
// It is optional; a nil callback does not install a prompt catalog command. // It is optional independently of the other catalogs; nil omits only this
ListPrompts func(context.Context, ckconfig.Selection, I, S) (commandkit.Acquired[[]promptkit.PromptIdentity], error) // catalog command.
// ListProfiles lists profile IDs after input and state preparation. ListPrompts func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[[]promptkit.PromptIdentity], error)
// It is optional; a nil callback does not install a profile catalog command. // ListProfiles lists profile IDs after input and state preparation. It is
ListProfiles func(context.Context, ckconfig.Selection, I, S) (commandkit.Acquired[[]string], error) // optional independently of the other catalogs; nil omits only this catalog
// command.
ListProfiles func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[[]string], error)
// ListBackends lists backend IDs after input and state preparation. It is
// optional independently of the other catalogs; nil omits only this catalog
// command.
ListBackends func(context.Context, CatalogRequest, I, S) (commandkit.Acquired[[]string], error)
// ListNamespaces lists native namespace names after input and state
// preparation. It is optional independently of the other catalogs; nil omits
// only this catalog command.
ListNamespaces func(context.Context, NamespaceRequest, I, S) (commandkit.Acquired[[]string], error)
// ListCollections lists collection information after input and state
// preparation. It is optional independently of the other catalogs; nil
// omits only this catalog command.
ListCollections func(context.Context, ckconfig.Selection, I, S) (commandkit.Acquired[[]promptkit.CollectionInfo], error)
issues []error issues []error
} }
@@ -225,11 +261,13 @@ func promptKitContextError(ctx context.Context) error {
return ctx.Err() return ctx.Err()
} }
// FromEngine supplies five PromptKit-backed callbacks: prompt and profile // FromEngine supplies eight PromptKit-backed callbacks: prompt, profile,
// catalogs, named prompt and profile inspection, and requirements discovery. // backend, namespace, and collection catalogs, plus named prompt and profile
// inspection and requirements discovery.
// The requirements callback forwards RequirementsRequest.Inspection to // The requirements callback forwards RequirementsRequest.Inspection to
// Engine.InspectRequirements. Concrete request validation is an independent // Engine.InspectRequirements. Concrete request validation is an independent
// capability and can be composed explicitly with WithRequestValidation. // capability and can be composed explicitly with WithRequestValidation. Smoke
// testing is also an explicit capability installed by its helper.
// FromEngine does not call the loader during construction. Each selected // FromEngine does not call the loader during construction. Each selected
// operation passes its context, supplied selection, initialized input, and // operation passes its context, supplied selection, initialized input, and
// shared invocation state to the loader before calling PromptKit's public // shared invocation state to the loader before calling PromptKit's public
@@ -242,22 +280,46 @@ 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, selection ckconfig.Selection, 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, selection, input, state, "", load) engine, _, diagnostics, 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: diagnostics}, err
} }
prompts, err := engine.ListPrompts(ctx) 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: diagnostics}, err
}, },
ListProfiles: func(ctx context.Context, selection ckconfig.Selection, 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, selection, input, state, "", load) engine, _, diagnostics, 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: diagnostics}, err
} }
profiles, err := engine.ListProfiles(ctx) profiles, err := engine.ListProfiles(ctx, request.Filter)
return commandkit.Acquired[[]string]{Value: profiles, Diagnostics: diagnostics}, err return commandkit.Acquired[[]string]{Value: profiles, Diagnostics: diagnostics}, err
}, },
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)
if err != nil {
return commandkit.Acquired[[]string]{Diagnostics: diagnostics}, err
}
backends, err := engine.ListBackends(ctx, request.Filter)
return commandkit.Acquired[[]string]{Value: backends, Diagnostics: diagnostics}, err
},
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)
if err != nil {
return commandkit.Acquired[[]string]{Diagnostics: diagnostics}, err
}
namespaces, err := engine.ListNamespaces(ctx, request.Kinds...)
return commandkit.Acquired[[]string]{Value: namespaces, Diagnostics: diagnostics}, err
},
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)
if err != nil {
return commandkit.Acquired[[]promptkit.CollectionInfo]{Diagnostics: diagnostics}, err
}
collections, err := engine.ListCollections(ctx)
return commandkit.Acquired[[]promptkit.CollectionInfo]{Value: collections, Diagnostics: diagnostics}, 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, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
if err != nil { if err != nil {

View File

@@ -458,25 +458,51 @@ func TestFromEngineListsNativeCatalogsWithInvocationValues(t *testing.T) {
} }
return engine, nil return engine, nil
}) })
if suite.ListPrompts == nil || suite.ListProfiles == nil { if suite.ListPrompts == nil || suite.ListProfiles == nil || suite.ListBackends == nil || suite.ListNamespaces == nil || suite.ListCollections == nil {
t.Fatalf("FromEngine did not supply both catalog callbacks: %#v", suite) t.Fatalf("FromEngine did not supply all five catalog callbacks: %#v", suite)
} }
wantPrompts, err := engine.ListPrompts(wantContext) catalogRequest := CatalogRequest{Selection: wantSelection, Filter: promptkit.NamespaceFilter{Mode: promptkit.NamespaceNamed, Name: "missing"}}
namespaceRequest := NamespaceRequest{Selection: wantSelection, Kinds: []promptkit.ResourceKind{promptkit.ResourcePrompt}}
wantPrompts, err := engine.ListPrompts(wantContext, catalogRequest.Filter)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
wantProfiles, err := engine.ListProfiles(wantContext) wantProfiles, err := engine.ListProfiles(wantContext, catalogRequest.Filter)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
prompts, err := suite.ListPrompts(wantContext, wantSelection, wantInput, wantState) wantBackends, err := engine.ListBackends(wantContext, catalogRequest.Filter)
if err != nil {
t.Fatal(err)
}
wantNamespaces, err := engine.ListNamespaces(wantContext, namespaceRequest.Kinds...)
if err != nil {
t.Fatal(err)
}
wantCollections, err := engine.ListCollections(wantContext)
if err != nil {
t.Fatal(err)
}
prompts, err := suite.ListPrompts(wantContext, catalogRequest, wantInput, wantState)
if err != nil || !reflect.DeepEqual(prompts.Value, wantPrompts) || len(prompts.Diagnostics) != 0 || loaderCalls != 1 { if err != nil || !reflect.DeepEqual(prompts.Value, wantPrompts) || len(prompts.Diagnostics) != 0 || loaderCalls != 1 {
t.Fatalf("prompt catalog = (%#v, %v), calls=%d, want %#v", prompts, err, loaderCalls, wantPrompts) t.Fatalf("prompt catalog = (%#v, %v), calls=%d, want %#v", prompts, err, loaderCalls, wantPrompts)
} }
profiles, err := suite.ListProfiles(wantContext, wantSelection, wantInput, wantState) profiles, err := suite.ListProfiles(wantContext, catalogRequest, wantInput, wantState)
if err != nil || !reflect.DeepEqual(profiles.Value, wantProfiles) || len(profiles.Diagnostics) != 0 || loaderCalls != 2 { if err != nil || !reflect.DeepEqual(profiles.Value, wantProfiles) || len(profiles.Diagnostics) != 0 || loaderCalls != 2 {
t.Fatalf("profile catalog = (%#v, %v), calls=%d, want %#v", profiles, err, loaderCalls, wantProfiles) t.Fatalf("profile catalog = (%#v, %v), calls=%d, want %#v", profiles, err, loaderCalls, wantProfiles)
} }
backends, err := suite.ListBackends(wantContext, catalogRequest, wantInput, wantState)
if err != nil || !reflect.DeepEqual(backends.Value, wantBackends) || len(backends.Diagnostics) != 0 || loaderCalls != 3 {
t.Fatalf("backend catalog = (%#v, %v), calls=%d, want %#v", backends, err, loaderCalls, wantBackends)
}
namespaces, err := suite.ListNamespaces(wantContext, namespaceRequest, wantInput, wantState)
if err != nil || !reflect.DeepEqual(namespaces.Value, wantNamespaces) || len(namespaces.Diagnostics) != 0 || loaderCalls != 4 {
t.Fatalf("namespace catalog = (%#v, %v), calls=%d, want %#v", namespaces, err, loaderCalls, wantNamespaces)
}
collections, err := suite.ListCollections(wantContext, wantSelection, wantInput, wantState)
if err != nil || !reflect.DeepEqual(collections.Value, wantCollections) || len(collections.Diagnostics) != 0 || loaderCalls != 5 {
t.Fatalf("collection catalog = (%#v, %v), calls=%d, want %#v", collections, err, loaderCalls, wantCollections)
}
} }
func TestFromEngineCatalogsRetainLoadDiagnosticsAndErrorIdentity(t *testing.T) { func TestFromEngineCatalogsRetainLoadDiagnosticsAndErrorIdentity(t *testing.T) {
@@ -492,7 +518,7 @@ func TestFromEngineCatalogsRetainLoadDiagnosticsAndErrorIdentity(t *testing.T) {
call func() (int, error) call func() (int, error)
}{ }{
{name: "prompts", call: func() (int, error) { {name: "prompts", call: func() (int, error) {
acquired, err := suite.ListPrompts(context.Background(), ckconfig.Selection{}, 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.Diagnostics) == 0 {
t.Errorf("prompt load failure = (%#v, %v)", acquired, err) t.Errorf("prompt load failure = (%#v, %v)", acquired, err)
@@ -500,13 +526,37 @@ func TestFromEngineCatalogsRetainLoadDiagnosticsAndErrorIdentity(t *testing.T) {
return len(acquired.Diagnostics), err return len(acquired.Diagnostics), err
}}, }},
{name: "profiles", call: func() (int, error) { {name: "profiles", call: func() (int, error) {
acquired, err := suite.ListProfiles(context.Background(), ckconfig.Selection{}, 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.Diagnostics) == 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), err
}}, }},
{name: "backends", call: func() (int, error) {
acquired, err := suite.ListBackends(context.Background(), CatalogRequest{}, 0, 0)
var retained *configkit.LoadError
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
t.Errorf("backend load failure = (%#v, %v)", acquired, err)
}
return len(acquired.Diagnostics), err
}},
{name: "namespaces", call: func() (int, error) {
acquired, err := suite.ListNamespaces(context.Background(), NamespaceRequest{}, 0, 0)
var retained *configkit.LoadError
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
t.Errorf("namespace load failure = (%#v, %v)", acquired, err)
}
return len(acquired.Diagnostics), err
}},
{name: "collections", call: func() (int, error) {
acquired, err := suite.ListCollections(context.Background(), ckconfig.Selection{}, 0, 0)
var retained *configkit.LoadError
if !errors.As(err, &retained) || retained != loadErr || len(acquired.Diagnostics) == 0 {
t.Errorf("collection load failure = (%#v, %v)", acquired, err)
}
return len(acquired.Diagnostics), err
}},
} { } {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
diagnosticCount, err := test.call() diagnosticCount, err := test.call()
@@ -525,11 +575,23 @@ func TestFromEngineCatalogsHonorCancellationAfterLoad(t *testing.T) {
call func(Suite[int, int], context.Context) error call func(Suite[int, int], context.Context) error
}{ }{
{name: "prompts", call: func(suite Suite[int, int], ctx context.Context) error { {name: "prompts", call: func(suite Suite[int, int], ctx context.Context) error {
_, err := suite.ListPrompts(ctx, ckconfig.Selection{}, 0, 0) _, err := suite.ListPrompts(ctx, CatalogRequest{}, 0, 0)
return err return err
}}, }},
{name: "profiles", call: func(suite Suite[int, int], ctx context.Context) error { {name: "profiles", call: func(suite Suite[int, int], ctx context.Context) error {
_, err := suite.ListProfiles(ctx, ckconfig.Selection{}, 0, 0) _, err := suite.ListProfiles(ctx, CatalogRequest{}, 0, 0)
return err
}},
{name: "backends", call: func(suite Suite[int, int], ctx context.Context) error {
_, err := suite.ListBackends(ctx, CatalogRequest{}, 0, 0)
return err
}},
{name: "namespaces", call: func(suite Suite[int, int], ctx context.Context) error {
_, err := suite.ListNamespaces(ctx, NamespaceRequest{}, 0, 0)
return err
}},
{name: "collections", call: func(suite Suite[int, int], ctx context.Context) error {
_, err := suite.ListCollections(ctx, ckconfig.Selection{}, 0, 0)
return err return err
}}, }},
} { } {
@@ -545,7 +607,7 @@ func TestFromEngineCatalogsHonorCancellationAfterLoad(t *testing.T) {
} }
}) })
} }
if loaderCalls != 2 { if loaderCalls != 5 {
t.Fatalf("catalog loaders called = %d, want one per provider", loaderCalls) t.Fatalf("catalog loaders called = %d, want one per provider", loaderCalls)
} }
} }

View File

@@ -310,7 +310,7 @@ 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],
list func(context.Context, ckconfig.Selection, 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 {
@@ -328,7 +328,7 @@ func runCatalogList[T any, I, S any](
} }
} }
acquired, err := list(invocation.Context, input.selection, input.selected, state) acquired, err := list(invocation.Context, CatalogRequest{Selection: input.selection}, input.selected, state)
if err != nil { if err != nil {
return definitionFailure(err, acquired.Diagnostics) return definitionFailure(err, acquired.Diagnostics)
} }

View File

@@ -56,10 +56,10 @@ func TestCommandBuildsIndependentOptionalTrees(t *testing.T) {
InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) { InspectRequirements: func(context.Context, RequirementsRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.RequirementInspection], error) {
return commandkit.Acquired[*promptkit.RequirementInspection]{}, nil return commandkit.Acquired[*promptkit.RequirementInspection]{}, nil
}, },
ListPrompts: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil
}, },
ListProfiles: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]string], error) { ListProfiles: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]string], error) {
return commandkit.Acquired[[]string]{}, nil return commandkit.Acquired[[]string]{}, nil
}, },
}) })
@@ -232,12 +232,12 @@ func TestRequirementsCommandUsageFailuresPrecedeApplicationPreparation(t *testin
func TestCatalogCommandTreesAreIndependentAndOmitShow(t *testing.T) { func TestCatalogCommandTreesAreIndependentAndOmitShow(t *testing.T) {
prompts := Command(Suite[struct{}, struct{}]{ prompts := Command(Suite[struct{}, struct{}]{
ListPrompts: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil
}, },
}) })
profiles := Command(Suite[struct{}, struct{}]{ profiles := Command(Suite[struct{}, struct{}]{
ListProfiles: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]string], error) { ListProfiles: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]string], error) {
return commandkit.Acquired[[]string]{}, nil return commandkit.Acquired[[]string]{}, nil
}, },
}) })
@@ -264,7 +264,7 @@ func TestCatalogCommandTreesAreIndependentAndOmitShow(t *testing.T) {
InspectPrompt: func(context.Context, PromptRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.PromptInspection], error) { InspectPrompt: func(context.Context, PromptRequest, struct{}, struct{}) (commandkit.Acquired[*promptkit.PromptInspection], error) {
return commandkit.Acquired[*promptkit.PromptInspection]{}, nil return commandkit.Acquired[*promptkit.PromptInspection]{}, nil
}, },
ListPrompts: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil
}, },
}) })
@@ -298,9 +298,9 @@ func TestCatalogListUsesPreparedInputAndPreflightsFormat(t *testing.T) {
} }
return nil return nil
}, },
ListPrompts: func(_ context.Context, selection ckconfig.Selection, value input, gotState state) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { ListPrompts: func(_ context.Context, request CatalogRequest, value input, gotState state) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
providerCalls++ providerCalls++
received, receivedSelection, receivedState = value, selection, gotState received, receivedSelection, receivedState = value, request.Selection, gotState
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing", Version: testStringPointer("1")}}}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{{ID: "briefing", Version: testStringPointer("1")}}}, nil
}, },
} }
@@ -372,13 +372,13 @@ func TestCatalogLifecycleCapturesSelectorsAndRefreshesInvocationValues(t *testin
} }
return nil return nil
}, },
ListPrompts: func(_ context.Context, selection ckconfig.Selection, got input, gotState *state) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { ListPrompts: func(_ context.Context, request CatalogRequest, got input, gotState *state) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
providers++ providers++
if gotState == nil || gotState.run != providers { if gotState == nil || gotState.run != providers {
t.Errorf("provider state = %#v at invocation %d", gotState, providers) t.Errorf("provider state = %#v at invocation %d", gotState, providers)
} }
if providers == 1 && (!selection.Config.Set || selection.Config.Value != "" || !selection.Overlay.Set || selection.Overlay.Value != "" || !got.campaign.Set || !got.enabled.Set || !got.limit.Set || !got.labels.Set) { if providers == 1 && (!request.Selection.Config.Set || request.Selection.Config.Value != "" || !request.Selection.Overlay.Set || request.Selection.Overlay.Value != "" || !got.campaign.Set || !got.enabled.Set || !got.limit.Set || !got.labels.Set) {
t.Errorf("first provider values = selection %#v input %#v", selection, got) t.Errorf("first provider values = selection %#v input %#v", request.Selection, got)
} }
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{}}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{}}, nil
}, },
@@ -424,9 +424,9 @@ func TestCatalogLifecycleCapturesSelectorsAndRefreshesInvocationValues(t *testin
func TestCatalogNilFactoriesSupplyZeroValues(t *testing.T) { func TestCatalogNilFactoriesSupplyZeroValues(t *testing.T) {
app := newPromptTestApp(t, Command(Suite[*struct{}, *struct{}]{ app := newPromptTestApp(t, Command(Suite[*struct{}, *struct{}]{
ListPrompts: func(_ context.Context, selection ckconfig.Selection, input, state *struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { ListPrompts: func(_ context.Context, request CatalogRequest, input, state *struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
if input != nil || state != nil || selection.Config.Set || selection.Overlay.Set { if input != nil || state != nil || request.Selection.Config.Set || request.Selection.Overlay.Set {
t.Errorf("nil-factory values = selection %#v input %#v state %#v", selection, input, state) t.Errorf("nil-factory values = selection %#v input %#v state %#v", request.Selection, input, state)
} }
return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil
}, },
@@ -446,10 +446,10 @@ func TestCatalogCommandsRenderNativeValuesAndEmptySequences(t *testing.T) {
} }
profiles := []string{"later", "001", "a: true", "later"} profiles := []string{"later", "001", "a: true", "later"}
suite := Suite[struct{}, struct{}]{ suite := Suite[struct{}, struct{}]{
ListPrompts: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: prompts}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: prompts}, nil
}, },
ListProfiles: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]string], error) { ListProfiles: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]string], error) {
return commandkit.Acquired[[]string]{Value: profiles}, nil return commandkit.Acquired[[]string]{Value: profiles}, nil
}, },
} }
@@ -502,14 +502,14 @@ func TestCatalogCommandsRenderNativeValuesAndEmptySequences(t *testing.T) {
promptEmptyCalls, profileEmptyCalls := 0, 0 promptEmptyCalls, profileEmptyCalls := 0, 0
emptyApp := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ emptyApp := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{
ListPrompts: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
promptEmptyCalls++ promptEmptyCalls++
if promptEmptyCalls%2 == 0 { if promptEmptyCalls%2 == 0 {
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{}}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: []promptkit.PromptIdentity{}}, nil
} }
return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: nil}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{Value: nil}, nil
}, },
ListProfiles: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]string], error) { ListProfiles: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]string], error) {
profileEmptyCalls++ profileEmptyCalls++
if profileEmptyCalls%2 == 0 { if profileEmptyCalls%2 == 0 {
return commandkit.Acquired[[]string]{Value: []string{}}, nil return commandkit.Acquired[[]string]{Value: []string{}}, nil
@@ -530,7 +530,7 @@ func TestCatalogCommandsRenderNativeValuesAndEmptySequences(t *testing.T) {
func TestCatalogRejectsOperandsAndUnavailableSelectors(t *testing.T) { func TestCatalogRejectsOperandsAndUnavailableSelectors(t *testing.T) {
calls := 0 calls := 0
app := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, ckconfig.Selection, 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) {
calls++ calls++
return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil return commandkit.Acquired[[]promptkit.PromptIdentity]{}, nil
}})) }}))
@@ -564,7 +564,7 @@ func TestCatalogFailuresRetainDiagnosticsAndDeliveryState(t *testing.T) {
return false 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, ckconfig.Selection, 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}}, providerErr
}})) }}))
outcome := app.Execute(context.Background(), []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: io.Discard, Err: io.Discard}) outcome := app.Execute(context.Background(), []string{"promptkit", "list", "prompts"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
@@ -573,7 +573,7 @@ func TestCatalogFailuresRetainDiagnosticsAndDeliveryState(t *testing.T) {
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
canceled := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, ckconfig.Selection, 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
}})) }}))
@@ -593,7 +593,7 @@ func TestCatalogFailuresRetainDiagnosticsAndDeliveryState(t *testing.T) {
{name: "short write", writer: partialReportWriter{}, want: io.ErrShortWrite}, {name: "short write", writer: partialReportWriter{}, want: io.ErrShortWrite},
} { } {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
writerApp := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, ckconfig.Selection, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) { writerApp := newPromptTestApp(t, Command(Suite[struct{}, struct{}]{ListPrompts: func(context.Context, CatalogRequest, struct{}, struct{}) (commandkit.Acquired[[]promptkit.PromptIdentity], error) {
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})