Capture PromptKit selectors before retaining callbacks
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
Reviewed on 2026-10-11, against committed implementation
|
Reviewed on 2026-10-11, against committed implementation
|
||||||
`cc574915ee17e2d738f27874d2c8857aa1f51a47`. The implementation sprint starts
|
`cc574915ee17e2d738f27874d2c8857aa1f51a47`. The implementation sprint starts
|
||||||
after planning commit `0d23676144ab1ce5047924e98f1ab6a2a9d6aeb6`.
|
after planning commit `0d23676144ab1ce5047924e98f1ab6a2a9d6aeb6`.
|
||||||
All issues below are open. P1 identifies a functional contract violation; P2
|
Completion status and evidence are recorded per issue below. P1 identifies a functional contract violation; P2
|
||||||
identifies architecture or documentation corrections; P3 identifies smaller
|
identifies architecture or documentation corrections; P3 identifies smaller
|
||||||
maintenance corrections. These priorities do not add publication gates.
|
maintenance corrections. These priorities do not add publication gates.
|
||||||
|
|
||||||
@@ -38,7 +38,14 @@ provider caches, or permanent validation gates to resolve these issues.
|
|||||||
|
|
||||||
## R1: Capture PromptKit selector handles before retaining the suite
|
## R1: Capture PromptKit selector handles before retaining the suite
|
||||||
|
|
||||||
**Status:** Open. **Priority:** P1.
|
**Status:** Completed (2026-10-11). **Priority:** P1.
|
||||||
|
|
||||||
|
**Completed work:** the enabled family copies its selector-handle collection
|
||||||
|
once, preserving original identities for both generated commands and retained
|
||||||
|
callbacks. Empty-suite behavior is unchanged. The public consumer regression
|
||||||
|
replaces a source element after capture and exercises prompt/profile inspection
|
||||||
|
in two apps reusing the declaration. It failed before the fix and passes after
|
||||||
|
it; the complete ckprompt suite passes offline with Go 1.26.8.
|
||||||
|
|
||||||
**Evidence:** [ckprompt/command.go](../../integrations/ckprompt/command.go),
|
**Evidence:** [ckprompt/command.go](../../integrations/ckprompt/command.go),
|
||||||
`Command`, `newDefinitionCommand`, and `prepareDefinitionInput`.
|
`Command`, `newDefinitionCommand`, and `prepareDefinitionInput`.
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ func Command[I, S any](suite Suite[I, S]) commandkit.Command {
|
|||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suite.Selectors = append([]commandkit.Flag[I](nil), suite.Selectors...)
|
||||||
showCommands := make([]commandkit.Command, 0, 2)
|
showCommands := make([]commandkit.Command, 0, 2)
|
||||||
if suite.InspectPrompt != nil {
|
if suite.InspectPrompt != nil {
|
||||||
showCommands = append(showCommands, newDefinitionCommand(suite, "prompt", true))
|
showCommands = append(showCommands, newDefinitionCommand(suite, "prompt", true))
|
||||||
|
|||||||
@@ -17,6 +17,55 @@ import (
|
|||||||
type compositionInput struct{ Campaign commandkit.Supplied[string] }
|
type compositionInput struct{ Campaign commandkit.Supplied[string] }
|
||||||
type compositionState struct{ token int }
|
type compositionState struct{ token int }
|
||||||
|
|
||||||
|
func TestPublicDefinitionCommandsCaptureSelectorHandles(t *testing.T) {
|
||||||
|
newFlag := func() commandkit.Flag[compositionInput] {
|
||||||
|
return commandkit.ValueFlag(commandkit.FlagMeta{Name: "campaign", Required: true}, (*string)(nil),
|
||||||
|
func(value string) (string, error) { return value, nil },
|
||||||
|
func(input *compositionInput, value commandkit.Supplied[string]) { input.Campaign = value })
|
||||||
|
}
|
||||||
|
selectors := []commandkit.Flag[compositionInput]{newFlag()}
|
||||||
|
calls := 0
|
||||||
|
checkInput := func(input compositionInput) {
|
||||||
|
t.Helper()
|
||||||
|
calls++
|
||||||
|
if !input.Campaign.Set || input.Campaign.Value != "spring" {
|
||||||
|
t.Errorf("captured selector value = %#v", input.Campaign)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declaration := ckprompt.Command(ckprompt.Suite[compositionInput, struct{}]{
|
||||||
|
Selectors: selectors,
|
||||||
|
InspectPrompt: func(_ context.Context, request ckprompt.PromptRequest, input compositionInput, _ struct{}) (commandkit.Acquired[*promptkit.PromptInspection], error) {
|
||||||
|
checkInput(input)
|
||||||
|
return commandkit.Acquired[*promptkit.PromptInspection]{Value: &promptkit.PromptInspection{PromptID: request.ID}}, nil
|
||||||
|
},
|
||||||
|
InspectProfile: func(_ context.Context, request ckprompt.ProfileRequest, input compositionInput, _ struct{}) (commandkit.Acquired[*promptkit.ProfileInspection], error) {
|
||||||
|
checkInput(input)
|
||||||
|
return commandkit.Acquired[*promptkit.ProfileInspection]{Value: &promptkit.ProfileInspection{ProfileID: request.ID}}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
first, err := commandkit.New(commandkit.Identity{Name: "first"}, declaration)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// A separately constructed lookalike must not replace the captured binding.
|
||||||
|
selectors[0] = newFlag()
|
||||||
|
second, err := commandkit.New(commandkit.Identity{Name: "second"}, declaration)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, app := range []*commandkit.App{first, second} {
|
||||||
|
for _, resource := range []string{"prompt", "profile"} {
|
||||||
|
outcome := app.Execute(context.Background(), []string{"promptkit", "show", resource, "briefing", "--campaign", "spring"}, commandkit.Streams{Out: io.Discard, Err: io.Discard})
|
||||||
|
if outcome.Kind != commandkit.OutcomeSuccess {
|
||||||
|
t.Fatalf("%s after selector edit: %#v", resource, outcome)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if calls != 4 {
|
||||||
|
t.Fatalf("inspection calls = %d, want 4", calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPublicConfigurationAndDefinitionFamiliesRemainIndependent(t *testing.T) {
|
func TestPublicConfigurationAndDefinitionFamiliesRemainIndependent(t *testing.T) {
|
||||||
inputs, err := promptkit.FilesystemInputs("composition fixture", fstest.MapFS{
|
inputs, err := promptkit.FilesystemInputs("composition fixture", fstest.MapFS{
|
||||||
"config/prompts.yml": &fstest.MapFile{Data: []byte(`prompts:
|
"config/prompts.yml": &fstest.MapFile{Data: []byte(`prompts:
|
||||||
|
|||||||
Reference in New Issue
Block a user